Jest: Mock implementation not working

Created on 11 Apr 2018  路  7Comments  路  Source: facebook/jest

Do you want to request a _feature_ or report a _bug_?

A bug, maybe?

What is the current behavior?

I'm trying to have multiple implementations of a mock to test different cases, but I'm not having success.

Consider the following snippet (parts omitted for brevity):

import { initRealm, startQueue } from '@/lib/realm'

class Main extends React.PureComponent {
  async componentDidMount() {
    await initRealm()
  }
}

And the test:

import React from 'react'

import { shallow } from 'enzyme'
import { initRealm } from '@/lib/realm'

import Main from './'

jest.mock('@/lib/realm', () => ({
  initRealm: jest.fn(),
})

describe('Main', () => {
  it('should successfully start realm', () => {
    initRealm.mockImplementation(() => Promise.resolve(true))
    const wrapper = shallow(<Main />)
  })

  it('should fail to init realm', () => {
    initRealm.mockImplementation(() => Promise.reject(new Error('failed to start realm')))
    const wrapper = shallow(<Main />)
  })
})

What is the expected behavior?

The mock implementation function to replace the jest.fn, so that the exception is thrown. But, instead, the test succeeds. I want to have multiple implementations of my mock so I can test both successful and fail cases.

Please provide your exact Jest configuration

"jest": {
  "preset": "react-native",
  "collectCoverage": true,
  "coverageDirectory": "./__coverage__",
  "coverageReporters": [
    "lcov"
  ],
  "collectCoverageFrom": [
    "src/**/*.{js,jsx,mjs}"
  ],
  "setupFiles": [
    "./__setup__"
  ],
  "transformIgnorePatterns": [
    "node_modules/(?!(jest-)?react-native|react-navigation)"
  ]
}
Environment:
  OS: Linux 4.13
  Node: 9.11.1
  Yarn: 1.5.1
  npm: 5.6.0
  Watchman: Not Found
  Xcode: N/A
  Android Studio: 3.0 AI-171.4443003

Am I missing something?

Needs More Info

Most helpful comment

If I do that way:

jest.mock('@/lib/realm')

initRealm.mockImplementationOnce(() => Promise.resolve('works'))
initRealm.mockImplementationOnce(() => Promise.reject(new Error('error thrown')))

describe('Main', () => {
  ...

It works. Why? And why it doesn't work inside the test?

Also: will Jest respect the order that the tests are defined in the describe clause, so these tests will not be flaky?

All 7 comments

If I do that way:

jest.mock('@/lib/realm')

initRealm.mockImplementationOnce(() => Promise.resolve('works'))
initRealm.mockImplementationOnce(() => Promise.reject(new Error('error thrown')))

describe('Main', () => {
  ...

It works. Why? And why it doesn't work inside the test?

Also: will Jest respect the order that the tests are defined in the describe clause, so these tests will not be flaky?

Tests are run in the order they are declared (unless you use test.concurrent).

Mind setting up a reproduction repo we can pull down to see the failure?

should probably test with expect(initRealm).toThrow();

I was having this issue when I was assigning the mocked method with someObj.mockedMethod = jest.fn().mockReturnValue(someVal) inside a data-driven test (using test.each). Calling someObj.mockedMethod.mockReset() at the end of the test (before recreating the mock in the next test run) fixed this issue for me.

Anything new with this? It is really a hassle

I'm mocking s3 package like this

jest.mock('aws-sdk/clients/s3');

using a __mock__/aws-sdk/clients/s3.js

s3.js __mock__

const S3 = jest.fn(() => ({
  putObject: jest.fn((data, callback) => {
    callback(null, {});
  }),
  upload: jest.fn((params, callback) => {
    callback(null, {});
  }),
  getSignedUrl: jest.fn((operation, params, callback) => {
    callback(null, '');
  }),
  getObject: jest.fn((params, callback) => {
    callback(null, '');
  }),
  headObject: jest.fn((params, callback) => {
    callback(null, true);
  }),
  createPresignedPost: jest.fn((params, callback) => {
    const { Bucket = process.env.BUCKET_MEDIA, Fields } = params;
    callback(null, {
      url: `https://s3.amazonaws.com/${Bucket}`,
      fields: {
        key: Fields.key,
        bucket: Bucket,
        success_action_status: 200,
        error: null,
      },
    });
  }),
}));

export default S3;

I'd like to override the implementation of getObject in some tests like this:

s3.getObject.mockImplementationOnce((params, callback) => {
    callback(null, {
      Body,
    });
  });

but mockImplementationOnce is never called

How should I proceed to fix this?

I've solved making s3 lazy instantiation

it looks like mockImplementation should be called before

Was this page helpful?
0 / 5 - 0 ratings