PThistle
PThistle

Reputation: 53

How can I jest-test a custom callback used with third-party API?

The simplified setup of my code is as follows:

App.js

class App extends React.Component {
  componentDidMount() {
    const { thirdPartyAPI } = this.props;
    thirdPartyAPI.field.onValueChanged(this.handleValueChange);
  }

  handleValueChange = (value) => {
    // callback to do complex validation stuff and suchlike
    // update state
  }

  render() {
    // display stuff
  }
}

App.test.js

each([...]).
  .test('App is rendered according to...',
    async (...) => {

    const mockApi = {
      field: {
        onValueChanged: ????
      }
    }

    const AppComponent = await shallow(
      <App thirdPartyAPI={mockApi} />,
    );
  });

OnValueChanged detects when a user altered the value outside the react-app. From a user-perspective this works, but the jest-test is giving me trouble.

Simply put: how do I test this?

I need to mock the API function, but I need to use the callback function handleValueChange as defined in App.js. I have lost track of the jest-tutorials I've read and watched, many of them very detailed, but none of them seem to apply to this particular situation.

Is it even possible to test this code? If so, how? And if not, how should I restructure my code so that I can test it?

If anyone can point me in the right direction, I would appreciate it.

Upvotes: 4

Views: 1094

Answers (1)

Brian Adams
Brian Adams

Reputation: 45840

If you pass a mock function to your component as thirdPartyAPI.field.onValueChanged then it will get called with this.handleValueChange.

Mock functions record everything they get called with, so you can then use the mock function to get this.handleValueChange using mockFn.mock.calls.

Once you have retrieved this.handleValueChange in your test, you can call it directly and verify that it works as expected.

Here is a simple working example:

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

class App extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = { val: 'original' };
  }

  componentDidMount() {
    const { thirdPartyAPI } = this.props;
    thirdPartyAPI.field.onValueChanged(this.handleValueChange);
  }

  handleValueChange = (value) => {
    this.setState({ val: value });
  }

  render() { return null; }
}

test('App', () => {
  const onValueChangedMock = jest.fn();
  const thirdPartyAPIMock = { field: { onValueChanged: onValueChangedMock } };

  const wrapper = shallow(<App thirdPartyAPI={thirdPartyAPIMock} />);
  expect(wrapper.state('val')).toBe('original');  // Success!

  const handleValueChange = onValueChangedMock.mock.calls[0][0];  // <= get handleValueChange...
  handleValueChange('updated');  // <= ...now call it directly

  expect(wrapper.state('val')).toBe('updated');  // Success!
})

Upvotes: 2

Related Questions