简体   繁体   中英

Jest: mocking conditional function calls

I am trying to write a test to make sure that, when appropriate, a particular function (in this case, a sentry function) is called with a particular message. However, when I write this test, it fails, and I get the following message. How to I properly mock the captureMessage function in handleError.test.js to make sure that it is properly called with the "this is an error message." string in handleError.js ? Thanks!

error message:

Error: expect(jest.fn())[.not].toHaveBeenCalledWith()

jest.fn() value must be a mock function or spy. Received: function: [Function captureMessage]

handleError.js:

import {captureMessage} from '@sentry/browser';

const handleError = (error) => {
  if (error.name === "ApiError") {
    captureMessage('this is an error message.');
  }
};

export default handleError;

handleError.test.js:

import {captureMessage} from '@sentry/browser';
import handleError from '../handleError';

class ApiError extends Error {
  constructor() {
    super();
    this.name = 'ApiError';
  }
}

test('When an ApiError is returned with no action type, sentry is notified', () => {
  const sampleError = new ApiError();
  handleError(sampleError);
  expect(captureMessage).toHaveBeenCalledWith('this is an error message.');
});

As @balzee mentioned, you have to actually spy on the method you want to make assertions about. That causes Jest to replace the method with a special spy function that keeps track of the parameters it is called with, how many times it was called, and so on.

You should also provide a mock implementation for that function, so that you don't actually call out to Sentry when running your unit tests.

Finally, when spying on a method, you first pass the object the method is on, and then the name of the method as a string. Jest then replaces that property on the object with a spy function, that will call the original function if no mock implementation is given.

Without referencing the object the function exists on, you would just be changing what a local function variable points to, from the original/real function to a jest spy function. That won't change the function that the code you are testing invokes, so the test will fail.

So the final test should be:

handleError.test.js:

import * as sentry from '@sentry/browser'; // CHANGED
import handleError from '../handleError';

class ApiError extends Error {
  constructor() {
    super();
    this.name = 'ApiError';
  }
}

// added this to remove any spies/mocks after the test
afterEach(() => {
  jest.restoreAllMocks();
});

test('When an ApiError is returned with no action type, sentry is notified', () => {
  const sampleError = new ApiError();
  // added next line
  jest.spyOn(sentry, 'captureMessage').mockImplementation(() => {});
  handleError(sampleError);
  // note the use of `sentry.captureMessage`, which is now a jest spy fn
  expect(sentry.captureMessage).toHaveBeenCalledWith('this is an error message.');
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM