【问题标题】:How to test the type of a thrown exception in Jest如何在 Jest 中测试抛出异常的类型
【发布时间】:2018-02-13 00:28:54
【问题描述】:

我正在使用一些代码来测试函数抛出的异常的类型(是 TypeError、ReferenceError 等吗?)。

我当前的测试框架是 AVA,我可以将其作为第二个参数 t.throws 方法进行测试,如下所示:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  t.is(error.message, 'UNKNOWN ERROR');
});

我开始用 Jest 重写我的测试,但找不到如何轻松地做到这一点。有没有可能?

【问题讨论】:

标签: javascript unit-testing jestjs


【解决方案1】:

我有同样的问题,唯一的解决方案是使用拒绝,但是如果我无法检查错误的来源,就会出现拒绝的问题。

这篇文章对我的问题有类似的解决方案。

JEST Received function did not throw, but HTTPError is thrown

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

我设法结合了一些答案并最终得到了这个:

it('should throw', async () => {
    await expect(service.methodName('some@email.com', 'unknown')).rejects.toThrow(
      HttpException,
    );
  });

【讨论】:

  • 这是针对基于被拒绝承诺的异常或错误。正是我想要的
  • 你拯救了我的一天!
【解决方案3】:

查看toThrow 方法。

您必须将代码包装在额外的函数回调中!

您应该同时检查:错误消息及其类型。

例如:

expect(
  () => { // additional function wrap
    yourCodeToTest();
  }
).toThrow(
  new RangeError('duplicate prevArray value: A')
);

由于额外的回调包装,代码不会立即运行,所以jest将能够捕获它。

您应始终检查错误消息,以确保您检查的是正确的throw 大小写,并且不会出现您的代码可能出现的另一个错误throw

检查错误类型也很好,因此客户端代码可能会依赖它。

【讨论】:

    【解决方案4】:

    在 Jest 中,您必须将函数传递给 expect(function).toThrow(<blank or type of error>)

    例子:

    test("Test description", () => {
      const t = () => {
        throw new TypeError();
      };
      expect(t).toThrow(TypeError);
    });
    

    或者如果您还想检查错误消息:

    test("Test description", () => {
      const t = () => {
        throw new TypeError("UNKNOWN ERROR");
      };
      expect(t).toThrow(TypeError);
      expect(t).toThrow("UNKNOWN ERROR");
    });
    

    如果您需要测试现有函数是否使用一组参数抛出,您必须将其包装在 expect() 中的匿名函数中。

    例子:

    test("Test description", () => {
      expect(() => {http.get(yourUrl, yourCallbackFn)}).toThrow(TypeError);
    });
    

    【讨论】:

    • 很好——请问为什么需要匿名函数包装器?有了包装器,它就可以工作,但没有它就不行。
    • @rags2riches 需要匿名函数,因为 expect(x).toThrow() 要求 x 是对抛出函数的引用。如果您改为通过expect(x()).toThrow(),JavaScript 将解析x(),这可能会立即导致错误,并且很可能会导致您的测试失败。
    • 问题中提供的 sn-p 是检查抛出异常的属性。这个答案只解决了 50% 的问题。不可避免地,我们将不得不使用try-catch 块。
    • @Mason - 提出问题的人想要检查错误的类型(“我需要测试函数抛出的异常的类型......”),但我认为这是个好主意还包括为那些想要检查的人检查错误消息。
    • @GerasimosRagavanis 当你测试异步函数时,你应该await expect(yourAsyncFn(...)).rejects.toThrow(...),就像 Douglas Caina here 回答的那样。
    【解决方案5】:

    除了Peter Danis' post,我只想强调他的解决方案中涉及“[传递]一个函数到expect(function).toThrow(空白或错误​​类型)”的部分。

    在 Jest 中,当您测试应该抛出错误的情况时,在被测函数的 expect() 包装中,您需要提供一个额外的箭头函数包装层才能使其工作。即

    错误(但大多数人的逻辑方法):

    expect(functionUnderTesting();).toThrow(ErrorTypeOrErrorMessage);
    

    对:

    expect(() => { functionUnderTesting(); }).toThrow(ErrorTypeOrErrorMessage);
    

    这很奇怪,但它应该可以成功运行测试。

    【讨论】:

    • 感谢您的回答。令人难以置信的是,Jest 的文档如何通过隐藏有关功能的重要信息使事情变得困难。
    【解决方案6】:

    The documentation 很清楚如何做到这一点。假设我有一个带有两个参数的函数,如果其中一个参数是null,它将引发错误。

    function concatStr(str1, str2) {
      const isStr1 = str1 === null
      const isStr2 = str2 === null
      if(isStr1 || isStr2) {
        throw "Parameters can't be null"
      }
      ... // Continue your code
    

    你的测试

    describe("errors", () => {
      it("should error if any is null", () => {
        // Notice that the expect has a function that returns the function under test
        expect(() => concatStr(null, "test")).toThrow()
      })
    })
    

    【讨论】:

      【解决方案7】:

      Modern Jest 允许您对被拒绝的值进行更多检查。例如:

      const request = Promise.reject({statusCode: 404})
      await expect(request).rejects.toMatchObject({ statusCode: 500 });
      

      将因错误而失败

      Error: expect(received).rejects.toMatchObject(expected)
      
      - Expected
      + Received
      
        Object {
      -   "statusCode": 500,
      +   "statusCode": 404,
        }
      

      【讨论】:

      • 重新“对被拒绝的值进行更多检查”:为什么有用?你能详细说明吗?最好由editing your answer没有“编辑”、“更新”或类似的)。
      • 非常感谢
      【解决方案8】:

      从我(虽然有限)接触到 Jest,我发现expect().toThrow() 适合测试特定类型的错误抛出:

      expect(() => functionUnderTest()).toThrow(TypeError);

      或者一个带有特定消息的错误被抛出:

      expect(() => functionUnderTest()).toThrow('Something bad happened!');

      如果你同时尝试两者,你会得到一个误报。例如,如果您的代码抛出RangeError('Something bad happened!'),则此测试将通过:

      expect(() => functionUnderTest()).toThrow(new TypeError('Something bad happened!'));

      The answer by bodolsog 建议使用 try/catch 很接近,但不要期望 true 为 false 以确保命中 catch 中的预期断言,您可以在测试开始时使用 expect.assertions(2) @ 987654329@ 是预期断言的数量。我觉得这更准确地描述了测试的意图。

      测试错误的类型消息的完整示例:

      describe('functionUnderTest', () => {
          it('should throw a specific type of error.', () => {
              expect.assertions(2);
      
              try {
                  functionUnderTest();
              } catch (error) {
                  expect(error).toBeInstanceOf(TypeError);
                  expect(error).toHaveProperty('message', 'Something bad happened!');
              }
          });
      });
      

      如果functionUnderTest()抛出错误,断言会被命中,但expect.assertions(2)会失败,测试也会失败。

      【讨论】:

      • 哦。我总是忘记 Jest 的预期多重断言功能(可能我个人认为它不是最直观的,但它绝对适用于这种情况!)干杯!
      • 当测试在catch 之外没有任何断言时,expect.hasAssertions() 可能是更好的选择,因为如果您添加/删除断言,则无需更新数字。
      • 另一种测试类型和消息的方法是使用开玩笑扩展项目中的toThrowWithMessage(type, message)
      • 解决方案在其他方面很好,但是如果 functionUnderTest 通过并且 catch 块下的期望从未测试过怎么办?我建议将期望移到主要功能下,以便始终对其进行测试
      • @IlmariKumpula 这就是为什么第一行定义了您期望的断言数量的原因。如果 functionUnderTest 通过并且从未进入 catch 块,则不会命中预期并且测试失败,因为没有进行两个断言。
      【解决方案9】:

      我使用了一个更简洁的版本:

      expect(() => {
        // Code block that should throw error
      }).toThrow(TypeError) // Or .toThrow('expectedErrorMessage')
      

      【讨论】:

        【解决方案10】:

        试试:

        expect(t).rejects.toThrow()
        

        【讨论】:

        • 为什么是try?没有尝试-但答案。如果这是答案,请详细说明。您在现有答案中添加了什么?
        • 我认为@Razim 说您应该尝试解决方案,而不是使用 try catch。
        【解决方案11】:

        这有点奇怪,但它有效,恕我直言,可读性很好:

        it('should throw Error with message \'UNKNOWN ERROR\' when no parameters were passed', () => {
          try {
              throwError();
              // Fail test if above expression doesn't throw anything.
              expect(true).toBe(false);
          } catch (e) {
              expect(e.message).toBe("UNKNOWN ERROR");
          }
        });
        

        Catch 块会捕获您的异常,然后您可以在引发的Error 上进行测试。如果不抛出预期的Error,则需要奇怪的expect(true).toBe(false); 才能使您的测试失败。否则,这条线永远无法到达(Error 应该在他们之前提出)。

        @Kenny Body 提出了一个更好的解决方案,如果您使用expect.assertions(),可以提高代码质量:

        it('should throw Error with message \'UNKNOWN ERROR\' when no parameters were passed', () => {
          expect.assertions(1);
          try {
              throwError();
          } catch (e) {
              expect(e.message).toBe("UNKNOWN ERROR");
          }
        });
        

        更多解释请参见原始答案:How to test the type of a thrown exception in Jest

        【讨论】:

        • 当 Jest 已经具有检查异常的 expect.toThrow() 方法时,这是一种非常冗长的异常测试方法:jestjs.io/docs/en/expect.html#tothrowerror
        • 是的,但它只测试类型,而不是消息或其他内容,问题是关于测试消息,而不是类型。
        • 哈。真的很喜欢这个,因为我的代码需要测试抛出错误的值,所以我需要实例。我会写像expect('here').not.toBe('here');这样的错误期望只是为了好玩:-)
        • @Valery 或:expect('to be').not.toBe('to be') 莎士比亚风格。
        • 你不需要做出虚假的断言来失败 - 你可以简单地使用fail(...) :)
        【解决方案12】:

        Jest 有一个方法,toThrow(error),用于测试函数在被调用时是否抛出。

        所以,在你的情况下,你应该这样称呼它:

        expect(t).toThrowError(TypeError);
        

        The documentation.

        【讨论】:

        • 如果模拟方法create 不是async,则不适用于jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });
        【解决方案13】:

        我自己没有尝试过,但我建议使用 Jest 的 toThrow 断言。所以我猜你的例子看起来像这样:

        it('should throw Error with message \'UNKNOWN ERROR\' when no parameters were passed', (t) => {
          const error = t.throws(() => {
            throwError();
          }, TypeError);
        
          expect(t).toThrowError('UNKNOWN ERROR');
          //or
          expect(t).toThrowError(TypeError);
        });
        

        同样,我还没有测试它,但我认为它应该可以工作。

        【讨论】:

          【解决方案14】:

          如果您使用的是Promises:

          await expect(Promise.reject(new HttpException('Error message', 402)))
            .rejects.toThrowError(HttpException);
          

          【讨论】:

            【解决方案15】:

            我最终为我们的 test-utils 库编写了一个方便的方法

            /**
             *  Utility method to test for a specific error class and message in Jest
             * @param {fn, expectedErrorClass, expectedErrorMessage }
             * @example   failTest({
                  fn: () => {
                    return new MyObject({
                      param: 'stuff'
                    })
                  },
                  expectedErrorClass: MyError,
                  expectedErrorMessage: 'stuff not yet implemented'
                })
             */
              failTest: ({ fn, expectedErrorClass, expectedErrorMessage }) => {
                try {
                  fn()
                  expect(true).toBeFalsy()
                } catch (err) {
                  let isExpectedErr = err instanceof expectedErrorClass
                  expect(isExpectedErr).toBeTruthy()
                  expect(err.message).toBe(expectedErrorMessage)
                }
              }
            

            【讨论】:

            猜你喜欢
            • 2018-09-11
            • 2020-06-22
            • 1970-01-01
            • 2019-07-27
            • 2020-11-24
            • 1970-01-01
            • 2023-01-25
            • 1970-01-01
            相关资源
            最近更新 更多