4

I'm testing a react-native Component which imports a Class LanguageStore. Currently the test fails because the component is instantiating this class which calls a private setter that is undefined in the scope of the test:

FAIL  src\modules\languageProvider\__tests__\LanguageProvider-test.js
  ● renders correctly

    TypeError: _this.strings.setLanguage is not a function

      at LanguageStore.setLanguage (src\modules\languageProvider\LanguageStore.js:25:15)
      at new LanguageProvider (src\modules\languageProvider\LanguageProvider.js:30:16)

Question:

How to hoist a jest dependency mock over actual dependency?

In order to resolve this I called a jest.mock according my to this answer How can I mock an ES6 module import using Jest?. But I get the same error as before because the test is calling the implementation of LanguageStore rather than the mock I created below - _this.strings.setLanguage is not a function:

import { View } from 'react-native';
import React from 'react';
import { shallow } from 'enzyme';
import renderer from 'react-test-renderer';
import connect from '../connect.js';
import LanguageProvider from '../LanguageProvider';
import LanguageStore from '../LanguageStore';

it('renders correctly', () => {

  const TestComponent = connect(Test);
  const strings = { test: 'Test' };
  const language = "en"

  const stringsMock = {
    setLanguage: jest.fn()
  };

  const mockSetLanguage = jest.fn();
  jest.mock('../LanguageStore', () => () => ({
    language: language,
    strings: stringsMock,
    setLanguage: mockSetLanguage,
  }));

  const wrapper = shallow(<LanguageProvider strings={strings} language="en"><Test /></LanguageProvider>);

  expect(wrapper.get(0)).toMatchSnapshot();

});


class Test extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <View />;
  }
}

This is a link to the test and related components and classes under test:

https://github.com/BrianJVarley/react-native-prototyping/blob/i18nProvider-feature/src/modules/languageProvider/tests/LanguageProvider-test.js

Brian Var
  • 6,029
  • 25
  • 114
  • 212

1 Answers1

6

Calling jest.mock within a test doesn't work.

You'll need to move your mock outside of the test and make sure your factory function doesn't have any external dependencies.

Something like this:

import { View } from 'react-native';
import React from 'react';
import { shallow } from 'enzyme';
import connect from '../connect.js';
import LanguageProvider from '../LanguageProvider';
import LanguageStore from '../LanguageStore';

jest.mock('../LanguageStore', () => {
  const language = "en"
  const stringsMock = {
    setLanguage: jest.fn()
  };
  const mockSetLanguage = jest.fn();

  return () => ({
    language,
    strings: stringsMock,
    setLanguage: mockSetLanguage,
  })
});

it('renders correctly', () => {
  const TestComponent = connect(Test);
  const strings = { test: 'Test' };
  const wrapper = shallow(<LanguageProvider strings={strings} language="en"><Test /></LanguageProvider>);
  expect(wrapper.get(0)).toMatchSnapshot();
});


class Test extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <View />;
  }
}
Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • ok moving the mock outside the test fixes the hoisting issue +1. So essentially a `jest.mock` must return the mock implementation also? I see I wasn't returning the mock also. – Brian Var Mar 19 '19 at 20:35
  • @BrianJ if you use a factory function (the optional second argument to `jest.mock`) then the factory function has to return the mock. (You actually *were* returning the mock since an [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) with no wrapping `{ }` automatically returns the result of its expression body) – Brian Adams Mar 19 '19 at 20:41
  • ok explains the reason for that, previously I had been supplying () instead of the factory function – Brian Var Mar 19 '19 at 21:22