55

I have a service class

Service.js

class Service {
}
export default new Service();

And I am trying to provide a mock implementation for this. If I use something like this:

jest.mock('./Service', () => { ... my mock stuff });

It works fine, however I'm not able to access any variables declared outside of the mock, which is a bit limiting as I'd like to reconfigure what the mock returns, etc.

I tried this (inspired by this other StackOverflow article: Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error)

import service from './Service';

jest.mock('./Service', () => jest.fn);

service.mockImplementation(() => {
    return { ... mock stuff }
);

Unfortunately when I am trying to run this, I get the below error:

TypeError: _Service2.default.mockImplementation is not a function
Janos
  • 1,987
  • 3
  • 17
  • 24
  • 1
    It's a React Native project (set up with create-react-native-application, aka expo), Jest is 20.0.4. Node: 7.2.0, Npm: 5.3.0 – Janos Aug 19 '17 at 13:05

8 Answers8

48

I had same problem as @Janos, the other answers didn't help either. You could do two things :

  1. If you need to mock only a function from Service, in your test file:

    import service from './Service';
    
    jest.mock('./Service', () => jest.fn());
    
    service.yourFunction = jest.fn(() => { /*your mock*/ })
    

     

  2. If you need to mock the entire Module:

    Say your service.js is in javascript/utils, create a javascript/utils/_mocks_ and inside it create a service.js file, you can then mock the entire class in this file, eg:

    const myObj = {foo: "bar"}
    
    const myFunction1 = jest.fn(() => { return Promise.resolve(myObj) })
    
    const  myFunction2 = ...
    
    module.exports = {
      myFunction1,
      myFunction2
    }
    

    then in your test file you just add:

    jest.mock('./javascript/utils/service')
    

    ...functions exported from the mockfile will be then hit through your test file execution.

Alexander Nied
  • 12,804
  • 4
  • 25
  • 45
guillaume
  • 651
  • 6
  • 14
17

The mock is equal to jest.fn. You need to call jest.fn to create a mocked function.

So this:

jest.mock('./Service', () => jest.fn);

Should be:

jest.mock('./Service', () => jest.fn());
deleteme
  • 349
  • 1
  • 3
13

ran into similar issues and resolved it by using .mockImplementationOnce

jest.mock('./Service', () => jest.fn()
  .mockImplementationOnce(() => {
    return { ... mock stuff }
  })
  .mockImplementationOnce(() => {
   return { ... mock other stuff }
  })
);

now when you run another test it will return the second mock object.

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Hendrik
  • 139
  • 1
  • 5
3

You need to store your mocked component in a variable with a name prefixed by "mock" and make sure you return an object with a default property as you import your Service from the default in your "main.js" file.

// Service.js
class Service {
}
export default new Service();

// main.test.js (main.js contains "import Service from './Service';")

const mockService = () => jest.fn();

jest.mock('./Service', () => {
    return {
        default: mockService
    }
});
maxletou
  • 1,355
  • 1
  • 8
  • 6
0

I had similar problem, and the cause was that ".spec.js" file had an

import jest from "jest-mock";

After removing this line, it worked.

Somnium
  • 1,059
  • 1
  • 9
  • 34
0

My mistake was that I was resetting the mock before each test. If you do that, be sure to reconfigure the mock implementation.

For example, change this:

  let value;
  let onPropertyChange: OnPropertyChangeCallback = jest.fn((changes: any) => {
      value = changes["testValue"];
    });

  const user = userEvent.setup();

  beforeEach(() => {
    jest.resetAllMocks();
  });

to this:

  let value;
  let onPropertyChange: OnPropertyChangeCallback;

  const user = userEvent.setup();

  beforeEach(() => {
    jest.resetAllMocks();

    onPropertyChange = jest.fn((changes: any) => {
      value = changes["testValue"];
    });
  });
Carl G
  • 17,394
  • 14
  • 91
  • 115
0

In my case mockImplementation didn't work because I copy paste tests and forgot to remove "async" keyword in this place it('test description', **async** () => {...}

0

Remove async from your test and DO NOT await expect!

WRONG

it.only('throws error on private visibility', async () => {
    await expect(getSteamData(mockSteamId)).rejects.toThrow(ErrorMessage.PRIVATE_VISIBILITY);
  });

RIGHT

it.only('throws error on private visibility', () => {
     expect(getSteamData(mockSteamId)).rejects.toThrow(ErrorMessage.PRIVATE_VISIBILITY);
  });