如何在Jest中测试引发的异常的类型


161

我正在使用一些代码,在其中我需要测试由函数引发的异常类型(是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,但找不到如何轻松地做到这一点。可能吗

Answers:


225

在Jest中,您必须将一个函数传递给Expect(function).toThrow(空白或错误​​类型)。

例:

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

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

例:

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

79

有点奇怪,但是可以正常工作,恕我直言,可读性强:

it('should throw Error with message \'UNKNOWN ERROR\' when no params 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阻止捕获您的异常,然后就可以对您提出的进行测试Errorexpect(true).toBe(false);如果您无法通过测试,则需要通过奇怪的测试Error。否则,这条线是永远无法到达的(Error应该在它们之前引起)。

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

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

请参阅原始答案以及更多说明:如何在Jest中测试引发的异常的类型


17
当Jest已经具有用于检查异常的Expect.toThrow()方法时,这是一种非常详尽的异常测试方法:jestjs.io/docs/en/expect.html#tothrowerror
gomisha

4
是的,但是它仅测试类型,而不测试消息或其他内容,并且问题是关于测试消息,而不是类型。
bodolsog

2
哈哈 确实像这样,因为我的代码需要测试抛出的错误的值,所以我需要实例。我会写一个错误的期望,就像expect('here').not.toBe('here');只是为了好玩:-)
Valery

4
@Valery or:expect('to be').not.toBe('to be')莎士比亚风格。
米歇尔·范德布朗克

2
最被低估的答案!
埃德温·依库丘乌·奥康沃

41

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

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

2
简短而精确。
Flajack

34

从我(虽然有限制)接触Jest的过程中,我发现expect().toThrow()如果只想测试抛出特定类型的错误,这是合适的:

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

或特定消息引发错误:

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

如果您尝试同时执行这两项操作,则会得到误报。例如,如果您的代码throw RangeError('Something bad happened!'),则此测试将通过:

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

bodolsog的建议使用try / catch的答案很接近,但是与其期望true为假以确保catch中的期望断言被击中,还可以expect.assertions(2)在测试开始时使用2期望断言的数量。我觉得这更准确地描述了测试的意图。

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

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预期的多个断言功能(可能我个人并没有发现它最直观),但在某些情况下肯定可以使用!)干杯!
kpollock19年

这对我来说很好。应该使用这个。
Ankit Tanna

expect.hasAssertions()当测试在外部没有任何断言时,这可能是一个更好的选择catch,因为如果添加/删除断言,则不必更新编号。
安德烈·萨西

12

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

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

  expect(t).toThrowError('UNKNOWN ERROR');
  //or
  expect(t).toThrowError(TypeError);
});

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


8

开玩笑的方法 toThrow(error)来测试函数在调用时是否抛出。

因此,在您的情况下,您应该这样称呼:

expect(t).toThrowError(TypeError);

该文档


1
不适用于这种情况:jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });如果嘲笑的方法create不是async
谢尔盖

7

现代玩笑使您可以对拒绝价值进行更多检查。例如:

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,
  }

6

有关如何执行此操作的文档很清楚。假设我有一个带有两个参数的函数,如果其中一个为,它将抛出错误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()
  })
})

4

如果您正在使用Promises:

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

这超级有帮助,谢谢您节省时间!!
Aditya Kresna Permana

3

我最终为我们的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)
    }
  }

使用Jests自己的功能可以完成相同的操作。请参阅我的答案以了解如何完成此操作-stackoverflow.com/a/58103698/3361387
Kenny Body

3

在彼得·丹尼斯(Peter Danis)的文章之后,他只是想强调他的解决方案的一部分,其中涉及“将函数传递到Expect(function).toThrow(空白或错误​​类型)”。

在Jest中,当您测试应该抛出错误的情况时,在被测函数的Expect()包装内,您需要提供一个附加的箭头函数包装层以使其起作用。即

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

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

对:

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

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.