karolis2017
karolis2017

Reputation: 2415

How do I mock and test chained function with jest?

I have this function chain in my components useEffect hook. I set the response/data to my component state so I can render the data in my component.

client
  .items()
  .type("client")
  .toObservable()
  .subscribe(response => {
    setState(response)
  })

How do I mock this function and call it before the component renders in my test? I'm using testing-library/react.

the response is {items: [{},...]}, somethingElse: {}

Upvotes: 21

Views: 20476

Answers (1)

Lin Du
Lin Du

Reputation: 102287

You can use mockFn.mockReturnThis() to do this.

client.ts, emulate a real third-party module

const client = {
  items: () => {
    return client;
  },
  type: (name: string) => {
    return client;
  },
  toObservable: () => {
    return client;
  },
  subscribe: handler => {
    handler();
    return client;
  }
};

export { client };

Use client.ts module in react component:

class Component {
  private props;
  constructor(props) {
    this.props = props;
  }
  public componentDidMount() {
    this.props.client
      .items()
      .type('client')
      .toObservable()
      .subscribe(response => {
        this.setState(response);
      });
  }
  private setState(state) {
    console.log(state);
  }
}

export { Component };

Unit test, mock chain method of the real client.ts module, inject the mocked client module into your component.

import { Component } from './';

const mockClient = {
  items: jest.fn().mockReturnThis(),
  type: jest.fn().mockReturnThis(),
  toObservable: jest.fn().mockReturnThis(),
  subscribe: jest.fn().mockReturnThis()
};

const mockProps = {
  client: mockClient
};

const component = new Component(mockProps);

describe('Component', () => {
  describe('#componentDidMount', () => {
    it('should mount the component and set state correctly', () => {
      const mockedResponse = { items: [{}], somethingElse: {} };
      mockClient.subscribe.mockImplementationOnce(handler => handler(mockedResponse));
      // tslint:disable-next-line: no-string-literal
      component['setState'] = jest.fn();
      component.componentDidMount();
      expect(mockClient.items().type).toBeCalledWith('client');
      // tslint:disable-next-line: no-string-literal
      expect(component['setState']).toBeCalledWith(mockedResponse);
    });
  });
});

Unit test result:

 PASS  src/mock-function/57719741/index.spec.ts
  Component
    #componentDidMount
      ✓ should mount the component and set state correctly (6ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.542s, estimated 2s

Upvotes: 32

Related Questions