Eli Nathan
Eli Nathan

Reputation: 1131

Test React component that depends on a Promise with Jest

I'm trying to learn a bit more about JS tests.

I've got a basic React component that fetch()'s data when it mounts.

When running the app, the component works as expected and gets the data.

However when testing with Jest, I can see that the call has been made but the promise is always rejected?

I've been following this example to produce the tests below.

Not sure about mocking promises with Jest, any pointers would be a huge help!

Component

import React from 'react';
import './App.scss';
import * as Utils from './Functions';
import Header from './components/Header';
import Loader from './components/Loader';
import Table from './components/Table';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    /*
    Initialise state:
      # Loading: true
    */
    this.state = {
      loading: true,
    };
  }

  /*
    When component mounts, 
    # Call function to get data
    # Set state with promise response
  */
  componentDidMount = () => {
    /* Function to grab data
      I've created a local express server to get around the cors issue
    */
    Utils.initData('http://localhost:8888/mock/all').then(data => {
      // Finally set state to reload component with new data
      this.setState({
        loading: false,
        teams: data,
      })
    })
  }  

  render() {
    const { loading, teams } = this.state;
    return (
      <div id="app">
        <Header />
        <div className="table">
          {loading && (<Loader />)}
          {!loading && (<Table data={teams} loading={loading} />)}
        </div>
      </div>
    );
  }
}

Functions

export const initData = (dataURL) => {
  try {
    // Get data using the Fetch API
    return fetch(dataURL).then(
      response => response.json()
    )
      // Then sanitize the data
      .then(data => sanitizeData(data));
  } catch (error) {
    console.warn(error);
    return error;
  }
}
export const sanitizeData = (data) => {
  console.log(data)
  // Do loads of stuff with the data
}

Test

import React from 'react';
import ReactDOM from 'react-dom';
import { shallow, mount } from 'enzyme';
import App from './App';
import Table from './components/Table';
import Header from './components/Header';
import * as Utils from './Functions';

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

describe('App', () => {
  it('- Renders the header', () => {
    const div = document.createElement('div');
    ReactDOM.render(<Header />, div);
    ReactDOM.unmountComponentAtNode(div);
  });

  it('- Renders the table', () => {
    const div = document.createElement('div');
    ReactDOM.render(<Table />, div);
    ReactDOM.unmountComponentAtNode(div);
  });

  it('- Renders the full app', () => {
    const div = document.createElement('div');
    ReactDOM.render(<App />, div);
    ReactDOM.unmountComponentAtNode(div);
  });
});

describe('Gets data', () => {
  it('fetches data from server when server returns a successful response', () => {
    const mockSuccessResponse = {};
    const mockJsonPromise = Promise.resolve(mockSuccessResponse);
    const mockFetchPromise = Promise.resolve({
      json: () => mockJsonPromise,
    });
    jest.spyOn(global, 'fetch').mockImplementation(() => mockFetchPromise); // 4
    const wrapper = shallow(<App />);                      
    expect(global.fetch).toHaveBeenCalledTimes(1);
    expect(global.fetch).toHaveBeenCalledWith('http://localhost:8888/mock/all');
  });
});

Error messages

I don't get any error in the app itself but while the test runs I get:

(node:4082) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'forEach' of undefined
[1] (node:4082) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
[1] (node:4082) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

The forEach mentioned above is from the sanitizeData() function and is there because the data param is {} when testing...

Upvotes: 1

Views: 2306

Answers (1)

Rishi Jodha
Rishi Jodha

Reputation: 186

You are returning {} in mockJsonPromise which gets passed on to sanitizeData() add hence the forEach loop is not working. Return a list with mock data instead.

    const mockSuccessResponse = {};
    const mockJsonPromise = Promise.resolve(mockSuccessResponse);
    const mockFetchPromise = Promise.resolve({
      json: () => mockJsonPromise,
    });
    jest.spyOn(global, 'fetch').mockImplementation(() => mockFetchPromise);

According to the above code response.json() will resolve to mockSuccessResponse which is {}

Upvotes: 2

Related Questions