0

Having some issues using spyOn for testing a method call inside my compose() block for my Reactjs app using recompose, redux, etc.

Basic layout is this:

// index.jsx
import { foo, baz } from './lib';
const enhance = compose(
  foo(),
  lifecycle({
      componentDidMount() {
        baz();
      }
  });
);
export const MyComp = (...);
const mapStateToProps = state => (...);
export connect(mapStateToProps)(enhance(MyComp));

// lib.js
export const foo = () => {
  lifecycle({
      componentDidMount() {
        bar();
      }
  });
}
export const bar = () => {};
export const baz = () => {};

//index.test.jsx
import * as lib from '.libs';

describe('Test', () => {
  const didMountSpy = jest.spyOn(MyComp.prototype, 'componentDidMount');
  const fooSpy = jest.spyOn(lib, 'foo');
  const barSpy = jest.spyOn(lib, 'bar');
  const bazSpy = jest.spyOn(lib, 'baz');

  const wrapper = mount(<MyComp ... />);

  expect(didMountSpy).toHaveBeenCalledTimes(1); // PASS
  expect(bazSpy).toHaveBeenCalledTimes(1); // PASS
  expect(fooSpy).toHaveBeenCalledTimes(1); // FAIL
  expect(barSpy).toHaveBeenCalledTimes(1); // FAIL
});

Strange part for me is if you look at baz(), I'm able to successfully expect the function call when it's not wrapped in another method in compse(). But I'm not able to expect foo(), bar(). My suspicion is that there is some weirdness with how enzyme/jest mocks the Reactjs lifecycle methods.

Has anyone one run into anything similar and have solutions to get spyOn to work for the nested methods under lifecycle.componentDidMount()?

Thanks!

Thiago Murakami
  • 965
  • 7
  • 11
jerbotron
  • 307
  • 1
  • 4
  • 14

2 Answers2

0

You are supposed to use jest.mock method for mocking imports

Follow these steps:

  • Create a folder named __mocks__ in the same folder where lib.js is located
  • Create a file named lib.js in __mocks__ folder and provide a mock implementation for foo, bar and baz methods/components
  • In index.test.jsx do not import lib.js. Also, before you import index.js in index.test add the following line

jest.mock('path-to-lib/lib.js')

Refer: https://jestjs.io/docs/en/manual-mocks

Vijay Purush
  • 322
  • 1
  • 12
0

fooSpy

fooSpy isn't called because foo gets called when enhance gets created which happens right when index.jsx is imported so foo has already been called by the time it gets wrapped in a spy to create fooSpy.

If you want to capture that foo was called you will need to delay the creation of enhance.

barSpy

bar() is defined locally in lib.js and it is being called directly within the definition of foo.

jest.spyOn replaces the module export of bar. Because the definition of foo is using bar directly and not the module export of bar, replacing the module export of bar with a spy does not affect the call within foo and barSpy is never called.

If you want to capture that bar was called you will need to use the module export of bar within the definition of foo.


Here is a working version of the test:

lib.js

import { lifecycle } from 'recompose';
import * as self from './lib';  // import the module into itself

export const foo = () =>
  lifecycle({
      componentDidMount() {
        self.bar();  // call the module export for bar()
      }
  });
export const bar = () => {};
export const baz = () => {};

index.jsx

import * as React from 'react';
import { compose, lifecycle } from 'recompose';

import { foo, baz } from './lib';

export class MyComp extends React.Component {
  render() { return (<div>My Comp</div>); }
}

// use a function to delay the creation of enhance()
export const initialize = () => {
  const enhance = compose(
    foo(),
    lifecycle({
        componentDidMount() {
          baz();
        }
    })
  );
  return enhance(MyComp);
}

index.test.jsx

import * as React from 'react';
import { mount } from 'enzyme';

import * as lib from './lib';
import { initialize } from './index';

test('MyComp', () => {
  const fooSpy = jest.spyOn(lib, 'foo');
  const barSpy = jest.spyOn(lib, 'bar');
  const bazSpy = jest.spyOn(lib, 'baz');

  const MyComp = initialize();  // this will create enhance() which will call foo()
  const didMountSpy = jest.spyOn(MyComp.prototype, 'componentDidMount');

  const wrapper = mount(<MyComp />);

  expect(didMountSpy).toHaveBeenCalledTimes(1); // PASS
  expect(bazSpy).toHaveBeenCalledTimes(1); // PASS
  expect(fooSpy).toHaveBeenCalledTimes(1); // PASS
  expect(barSpy).toHaveBeenCalledTimes(1); // PASS
});
Brian Adams
  • 43,011
  • 9
  • 113
  • 111