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


当前回答

我用了一个稍微简洁一点的版本:

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

其他回答

我自己没有尝试过,但我建议使用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);
});

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

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

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

expect(t).toThrowError(TypeError);

文档。

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

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

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

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

正确的:

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

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

开玩笑地说,你必须将一个函数传递给expect(函数)。toThrow(<空白或错误类型>)。

例子:

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);
});

一个好方法是创建自定义错误类并模拟它们。然后你可以断言任何你想要的东西。

MessedUpError.ts

type SomeCrazyErrorObject = {
  [key: string]: unknown,
}

class MessedUpError extends Error {
  private customErrorData: SomeCrazyErrorObject = {};

  constructor(err?: string, data?: SomeCrazyErrorObject) {
    super(err || 'You messed up');

    Object.entries(data ?? {}).forEach(([Key, value]) => {
      this.customErrorData[Key] = value;
    });
    Error.captureStackTrace(this, this.constructor);
  }

  logMe() {
    console.log(this.customErrorData);
  }
}

export default MessedUpError;

messedUpError.test.ts

import MessedUpError from './MessedUpError';

jest.mock('./MessedUpError', () => jest.fn().mockImplementation((...args: any[]) => ({
  constructor: args,
  log: () => {},
})));

type MessedUpErrorContructorParams = Expand<typeof MessedUpError['prototype']>
const MessedUpErrorMock = MessedUpError as unknown as jest.Mock<MessedUpError, [MessedUpErrorContructorParams]>;

const serverErrorContructorCall = (i = 0) => ({
  message: MessedUpErrorMock.mock.calls[i][0],
  ...MessedUpErrorMock.mock.calls[i][1] || {},
});

beforeEach(() => {
  MessedUpErrorMock.mockClear();
});

test('Should throw', async () => {
  try {
    await someFunctionThatShouldThrowMessedUpError();
  } catch {} finally {
    expect(MessedUpErrorMock).toHaveBeenCalledTimes(1);
    const constructorParams = serverErrorContructorCall();
    expect(constructorParams).toHaveProperty('message', 'You messed up');
    expect(constructorParams).toHaveProperty('customErrorProperty', 'someValue');
  }
});

断言总是在finally子句中。通过这种方式,它将始终被断言。即使测试没有抛出任何错误。