ChatGPT解决这个技术问题 Extra ChatGPT

How to properly make mock throw an error in Jest?

I'm testing my GraphQL api using Jest.

I'm using a separate test suit for each query/mutation

I have 2 tests (each one in a separate test suit) where I mock one function (namely, Meteor's callMethod) that is used in mutations.

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

When I console.log(result) I get

{ data: { forgotPassword: true } }

This behaviour is not what I want because in .mockReturnValue I throw an Error and therefore expect result to have an error object

Before this test, however, another is ran

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

And it works fine, the error is thrown

I guess the problem is that mock doesn't get reset after the test finishes. In my jest.conf.js I have clearMocks: true

Each test suit is in a separate file, and I mock functions before tests like this:

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() {
...

UPDATE

When I substituted .mockReturnValue with .mockImplementation everything worked out as expected:

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

But that doesn't explain why in another test .mockReturnValue works fine...

It looks like your mock is returning an error object, not throwing it. Without seeing your code that you are testing, I can only share the experience I had. I forgot to mock a function called in my mutation, which caused an error to be thrown unintentionally. Perhaps there is something similar happening for you?

e
eduardomoroni

Change .mockReturnValue with .mockImplementation:

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

in case you want to assert

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

If it's a promise you can also to .rejects www.jestjs.io/docs/en/asynchronous#resolves--rejects


cool. How to handle it and make and assert?
it throws the error but then the test fails because it has thrown an error. how do we assert?
g
gawkface

For promises, can use https://jestjs.io/docs/mock-function-api#mockfnmockrejectedvaluevalue

test('async test', async () => {
  const asyncMock = jest.fn().mockRejectedValue(new Error('Async error'));

  await asyncMock(); // throws "Async error"
});

For testing that error was thrown or not, can use https://eloquentcode.com/expect-a-function-to-throw-an-exception-in-jest

const func = () => {
  throw new Error('my error')
}
it('should throw an error', () => {
    expect(func).toThrow()
})

You would also need a try and catch in your expect otherwise it would not assert correctly. Can you please improve your answer or reply if I am missing something.
@MGDeveloper we dont need try-catch while unit testing and using toThrow() (jestjs.io/docs/expect#tothrowerror). If you try that in your tests, it should work. Can also test in here : codesandbox.io/s/jest-playground-forked-euewe?file=/src/… (sandbox content is transient though). I did edit the terminology from "handling" to "testing" if that was confusing
S
Sean W

For Angular + Jest:

import { throwError } from 'rxjs';

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

Technically this isn't a throw in the pure JS sense. You are configuring the mock to return a RXJS observable object which immediately emits an error notification. Still, maybe handy for folks to see here. The accepted answer certainly will make a mock throw an error. In all cases.
Only returning throw Error should be enough: yourMockInstance.mockImplementation(() => throwError('my error message'));
Actually using mockReturnValue is enough: mockInstance.mockReturnValue(throwError(() => new Error('my error message')))