Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test componentDidMount conditionally making API calls

Tags:

I'm using React in my application. I'm making an API call in my componentDidMount but it is conditional. My code in component is

componentDidMount() {
    if (!this.props.fetch) {
      fetchAPICall()
        .then(() => {
          /** Do something **/
        });
    }
  }

I've written test as :

it('should not fetch ', () => {
      const TFCRender = mount(<Component fetch />);
      const didMountSpy = jest.spyOn(TFCRender.prototype, 'componentDidMount');
      expect(didMountSpy).toHaveBeenCalledTimes(1);
      expect(fetchAPICall).toHaveBeenCalledTimes(0);
    });

The test is throwing me error as

TypeError: Cannot read property 'componentDidMount' of undefined

What am I doing wrong and what is the right way to test such case.

like image 952
Ajay Gaur Avatar asked May 15 '18 07:05

Ajay Gaur


People also ask

Can we make API call in componentDidMount?

Make the API Call in componentDidMount()You should populate data with AJAX calls in the componentDidMount lifecycle method. So you can use setState to update your component when the data is retrieved.

Why API call is recommended in componentDidMount ()?

Calling API in constructor() or componentWillMount() is not a syntax error but increases code complexity and hampers performance. So, to avoid unnecessary re-rendering and code complexity, it's better to call API after render(), i.e componentDidMount().

Can we make API call in componentDidUpdate?

An example of when to use componentDidUpdate() is when we need to call an external API on condition that the previous state and the current state have changed. The call to the API would be conditional to the state being changed. If there is no state change, no API is called.

Can we call API in componentWillMount?

Using componentWillMount() to Make API Calls One of the primary usages of componentWillMount() is to make API calls once the component is initiated and configure the values into the state. To make an API call, use an HttpClient such as Axios , or or you can use fetch() to trigger the AJAX call.


1 Answers

From the official docs, you need to spy the component before mounting it.

Following is a working example that I have created with create-react-app. I've also added some comments in the example code:

App.js

import { fetchAPICall } from './api';

class App extends Component {
  componentDidMount() {
    if (!this.props.fetch) {
      fetchAPICall().then(console.log);
    }
  }
  render() {
    return <div>Testing the result</div>;
  }
}

export default App;

api.js

export const fetchAPICall = () => {
  return Promise.resolve('Getting some data from the API endpoint');
};

App.test.js

import Component from './App';
import * as apis from './api'; // assuming you have a separate file for these APIs

// Mock the fetchAPICall, and since the data fetching is asynchronous 
// you have to mock its implementation with Promise.resolve()`
apis.fetchAPICall = jest.fn(() => Promise.resolve('test'));

describe('spyOn', () => {
  let didMountSpy; // Reusing the spy, and clear it with mockClear()
  afterEach(() => {
    didMountSpy.mockClear();
  });

  didMountSpy = jest.spyOn(Component.prototype, 'componentDidMount');

  test('should not fetch ', () => {
    // Ensure the componentDidMount haven't called yet.
    expect(didMountSpy).toHaveBeenCalledTimes(0);

    const TFCRender = mount(<Component fetch />);
    expect(didMountSpy).toHaveBeenCalledTimes(1);
    expect(apis.fetchAPICall).toHaveBeenCalledTimes(0);
  });

  test('should fetch', () => {
    expect(didMountSpy).toHaveBeenCalledTimes(0);

    const TFCRender = mount(<Component fetch={false} />);
    expect(didMountSpy).toHaveBeenCalledTimes(1);
    expect(apis.fetchAPICall).toHaveBeenCalledTimes(1);
  });
});

Not sure if this is the best practice, but this is how I usually write my own tests.

Hope this help!

like image 197
konekoya Avatar answered Oct 04 '22 15:10

konekoya