如何在Jest中正确地使模拟抛出错误?

207

我正在使用 Jest 测试我的 GraphQL api。

我为每个查询/变异使用单独的测试套件。

我有 2 个测试(每个测试套件分开),其中我模拟了一个函数(即 Meteor 的 callMethod),该函数在变异中使用。

  it('should throw error if email not found', async () => {
    callMethod
      .mockReturnValue(new Error('User not found [403]'))
      .mockName('callMethod');

    const query = FORGOT_PASSWORD_MUTATION;
    const params = { email: 'user@example.com' };

    const result = await simulateQuery({ query, params });

    console.log(result);

    // test logic
    expect(callMethod).toBeCalledWith({}, 'forgotPassword', {
      email: 'user@example.com',
    });

    // test resolvers
  });

当我执行 console.log(result) 时,我得到的结果是

{ data: { forgotPassword: true } }

这种行为并不是我想要的,因为在 .mockReturnValue 中我抛出了一个错误,因此期望 result 是一个错误对象。

然而,在这个测试之前还有另一个测试被运行。

 it('should throw an error if wrong credentials were provided', async () => {
    callMethod
      .mockReturnValue(new Error('cannot login'))
      .mockName('callMethod');

好的,它可以正常工作,但会抛出错误。

我猜问题在于测试完成后模拟(mock)没有被重置。jest.conf.js中我有clearMocks: true

每个测试套件都在单独的文件中,我在测试之前模拟函数如下:

import simulateQuery from '../../../helpers/simulate-query';

import callMethod from '../../../../imports/api/users/functions/auth/helpers/call-accounts-method';

import LOGIN_WITH_PASSWORD_MUTATION from './mutations/login-with-password';

jest.mock(
  '../../../../imports/api/users/functions/auth/helpers/call-accounts-method'
);

describe('loginWithPassword mutation', function() {
...

更新

当我使用.mockImplementation替换.mockReturnValue时,一切都按预期工作:

callMethod.mockImplementation(() => {
  throw new Error('User not found');
});

但这并不能解释为什么在另一个测试中.mockReturnValue能够正常运行...


1
看起来你的模拟对象是返回一个错误对象,而不是_抛出_它。如果没有看到你正在测试的代码,我只能分享我所经历的经验。我忘记了模拟在我的变异中调用的函数,这导致意外地抛出了一个错误。也许你遇到了类似的情况? - Rhuarc13
4个回答

350

使用.mockImplementation替换.mockReturnValue

    yourMockInstance.mockImplementation(() => {
      throw new Error();
    });

如果您想进行断言


   test('the fetch fails with an error', () => {
     return expect(fetchData()).rejects.toMatch('error');
   });

如果它是一个promise,你也可以使用.rejects www.jestjs.io/docs/en/asynchronous#resolves--rejects


1
酷。如何处理它并进行断言? - Dmytro
2
它抛出了错误,但测试失败了,因为它抛出了一个错误。我们该如何断言? - schlingel

40

2
你还需要在你的期望中添加try和catch,否则它将无法正确断言。如果我漏掉了一些东西,你能否改进你的答案或回复我。 - MG Developer
1
@MGDeveloper,我们在单元测试和使用toThrow()时不需要try-catch。如果您在测试中尝试这样做,它应该可以工作。您也可以在此处进行测试:https://codesandbox.io/s/jest-playground-forked-euewe?file=/src/sum.test.js:210-217(沙盒内容是短暂的)。如果术语“处理”令人困惑,我已将其更改为“测试”。 - gawkface
1
我发现mockRejectedValue在测试异步单元时非常有用,特别是当被测试的异步单元以我想要测试的特定方式处理抛出的异常时,在这种情况下不需要使用catch或toThrow()。 - CortexCompiler

17

对于 Angular + Jest:

import { throwError } from 'rxjs';

yourMockInstance.mockImplementation(() => {
  return throwError(new Error('my error message'));
});

2
从技术上讲,这不是纯JS意义上的“throw”。您正在配置模拟以返回一个RXJS可观察对象,该对象立即发出错误通知。尽管如此,对于大家来说可能很方便在这里看到。接受的答案肯定会使模拟抛出错误。在所有情况下。 - Zach Lysobey
只需返回 throw Error 即可:yourMockInstance.mockImplementation(() => throwError('my error message')); - manzapanza
实际上只需要使用mockReturnValuemockInstance.mockReturnValue(throwError(() => new Error('my error message'))). - RcoderNY

0

请确保在catch块或有条件的情况下添加throw new Error('网络错误或其他错误')

import fetchApi from '../src'

it("should throw error", async () => {
    const errorMessage: string = "Network Error";
    
    (axios.post as jest.Mock).mockRejectedValueOnce(new Error(errorMessage));
    
    expect(async () => await fetchApi()).rejects.toThrow(
          errorMessage
    );
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接