【问题标题】:Jest test fails when trying to test an asynchronous function throws尝试测试异步函数抛出时,Jest 测试失败
【发布时间】:2018-11-21 06:16:15
【问题描述】:

我有一个异步函数,我想同时测试:成功和失败。成功时函数返回一个字符串,失败时抛出。我在测试失败方面失败得很惨。这是我的代码:

  • getKmlFileName.test.js

我已通过注释失败的代码禁用并将结果添加到 cmets

    'use strict';

    const path = require('path');
    const fs = require('fs');

    const getKmlFilename = require('./getKmlFileName.js');

    const createGoodFolder = () => {
      const folderPath = fs.mkdtempSync('/tmp/test-getKmlFilename-');
      const fileDescriptor = fs.openSync(path.join(folderPath, 'doc.kml'), 'w');
      fs.closeSync(fileDescriptor);
      return folderPath;
    };

    const createEmptyFolder = () => fs.mkdtempSync('/tmp/test-getKmlFilename-');

    describe('/app/lib/getKmlFilename', () => {
      // Success tests
      test('Should return a KML filename', async () => {
        const result = await getKmlFilename(createGoodFolder());
        expect(result).toMatch(/\.kml$/);
      });

      // Failure tests
      test('Should throw if no KML files in folder', () => {
        // Expected one assertion to be called but received zero assertion calls.
        // expect.assertions(1);

        // expect(function).toThrow(undefined)
        // Received value must be a function, but instead "object" was found
        //return getKmlFilename(createEmptyFolder())
        // .catch(e => expect(e).toThrow());

        // expect(string)[.not].toMatch(expected)
        // string value must be a string.
        // Received:
        // object:
        // [Error: No valid KML file in /tmp/test-getKmlFilename-j2XxQ4]

        return getKmlFilename(createEmptyFolder())
          .catch(e => expect(e).toMatch('No valid KML file in'));
      });

      test('Should throw if no KML files in folder - try/catch version',
        async () => {
        // Expected one assertion to be called but received zero assertion calls.
        // expect.assertions(1);

        try {
          const result = await getKmlFilename(createEmptyFolder());
        } catch (e) {
          // Received value must be a function, but instead "object" was found
          // expect(e).toThrow();

          // expect(string)[.not].toMatch(expected)
          // string value must be a string.
          // Received:
          // object:
          // [Error: No valid KML file in /tmp/test-getKmlFilename-3JOUAX]
          expect(e).toMatch('No valid KML file in');
        }
      });

    });

如您所见,没有任何效果。我相信我的测试几乎完全复制了第一个失败测试的 Promises 示例和最后一个失败测试的 Async/Await 示例,但是没有一个有效。

我相信与 Jest 文档中示例的不同之处在于它们展示了如何测试函数 throws 以​​及如何测试 rejects 的 Promise。但是我的承诺通过抛出来拒绝。

检查节点控制台中的功能我得到了这个日志:

// import function
> getKml = require('./getKmlFileName.js')
[AsyncFunction: getKmlFilename]
// trying it with a proper folder shows we get a Promise
> getKml('/tmp/con')
Promise {
  <pending>,
  domain: 
   Domain {
     domain: null,
     _events: { error: [Function: debugDomainError] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] } }
// trying it with a failing folder shows it's a rejected promise which throws
> getKml('/tmp/sin')
Promise {
  <pending>,
  domain: 
   Domain {
     domain: null,
     _events: { error: [Function: debugDomainError] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] } }
> (node:10711) UnhandledPromiseRejectionWarning: Error: No valid KML file in /tmp/sin
    at getKmlFilename (/home/flc/soft/learning/2018.06.08,jest/getKmlFileName.js:14:11)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:228:7)
(node:10711) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:10711) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

从内联的 cmets 中可以看出,该函数正在做它应该做的事情,但是我不知道如何在 Jest 中测试它。任何帮助将不胜感激。

如果这里的代码看起来太复杂,我准备了一个repository,其中包含我学习Jest的不幸

2018.06.12 更新:

不知何故,我的信息被打乱了,丢失了第一部分,这是我正在尝试测试的实际代码,对此我深表歉意,这里是:

  • getKmlFileName.js

    'use strict';
    const globby = require('globby');
    const path = require('path');
    
    const getKmlFilename = async (workDir) => {
      const pattern = path.join(workDir, '**/*.kml');
      const files = await globby(pattern);
      if (files && files.length > 0) {
        // Return first KML found, if there are others (improbable), ignore them
        return path.basename(files[0]);
      } else {
        throw new Error(`No valid KML file in ${workDir}`);
      }
    };
    
    module.exports = getKmlFilename;
    

【问题讨论】:

  • 你是否总是返回一个 Promise(getKmlFilename 声明为异步)?
  • @Narigo 是的,你可以在我的更新中看到

标签: javascript async-await jestjs


【解决方案1】:

在您的第一次测试中:

return getKmlFilename(createEmptyFolder())
  .catch(e => expect(e).toMatch('No valid KML file in'));

如果 Promise 解决,它不会抱怨。

在第二次测试中

try {
  const result = await getKmlFilename(createEmptyFolder());
} catch (e) {
  ...
}

如果 Promise 解决,它也不会抱怨,因为它不会进入 catch 块。

要测试 Promise,请问自己以下问题:

  1. 承诺应该成功(解决)还是失败(拒绝)?
  2. 您的结果或拒绝值是Error 还是常规对象?

开玩笑,你应该可以这样做:

  1. 解析为常规对象:expect(yourThing()).resolves.toMatchSnapshot()
  2. 解决错误(从未见过): expect(yourThing()).resolves.toThrow(/something/)
  3. 拒绝错误: expect(yourThing()).rejects.toThrow(/something/)
  4. 拒绝常规对象(您确定要这样做吗?): expect(yourThing()).rejects.toMatchSnapshot()

请注意,异步函数总是返回一个值(一个 Promise 对象),因此“通常的”expect(() =&gt; yourThing()).toThrow() 将不起作用。您需要先等待 Promise 的结果(通过使用 resolvesrejects)然后进行测试。

【讨论】:

  • 失败的测试是测试失败的测试,成功的测试工作正常,是第一个。回答您的观点:1) 两者都是,首先测试测试成功,另外两个是我在文档中找到的两种不同的测试异步代码的方法。 2) 成功结果是一个字符串,拒绝结果是一个Error,正如您在我的更新中看到的那样。我的疑问是:Javascript async 会负责将 throw 包装在 reject 调用中吗?我以为是的。最后,我正在尝试测试是否失败,所以我不希望 promise 在我以后的测试中成功解决。为什么expect.assertions(1) 不起作用?
  • 终于有机会继续这个,第三种方法是我需要测试一个抛出的promise。谢谢。
  • 我猜我们所有人都是为了第三条路而来的。你能否让你的优秀答案的那部分更明显?
  • 这里rejects.toThrow(.....) 看起来不像是在开玩笑地尊重参数。无论我通过什么,它都会接受并通过测试。此外,如果承诺解决了,那么 expect(myPromise()).rejects.toThrow() 仍然会通过测试,但会抱怨 UnhandledPromiseRejectionWarning: Error: expect(received).rejects.toThrow() Received promise resolved instead of rejected
  • 请将您的第三个示例更改为await expect(yourThing()).rejects.toThrow(/yourErrorMessage/)
猜你喜欢
  • 2020-11-24
  • 1970-01-01
  • 2020-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多