Website Links

Tuesday, 23 April 2019

React - Testing with Jest

This post is just a basic of how to setup for Jest testing with React. There will be more posts in the future to cover different assertion types, how to integrate Jest tests into your CI pipeline and how to use Enzyme for rendering components. By default Jest is included in React, and it will pick up tests in either the __tests__ directory or with a filename ending in .test.js. There are different advantages to each of these. I personally feel like __tests__ is better for larger projects, but this may be down to my C# background where you'll commonly have a separate project for tests. As I tend to be of the opinion that you can always grow a program overtime I tend to favour this in all cases. However, the advantage of appending .test.js to the end of your filename is that your file can reside in the same directory as the source code you're testing, which means that import statements tend to be a bit less complicated. Whatever your decision, it's supported and Jest will pick up your test cases.

By default, your React app likely has an App.test.js file which I moved and renamed to __tests__/App.js. Now I was pretty bad and didn't write any test cases for a while, and went on developing without running this test... So, when I finally ran Jest tests using the command
npm test
I found my test was failing! The first cause of failure was that when I rendered my App on index.js I had actually surrounded it in a <BrowserRouter> tag, which meant that when <App> was rendered on its own, it was missing the <BrowserRouter> surrounding the <Switch> and <Route>s. So this test actually allowed me to refactor my code into a more readable and sensible structure... And that was just the basic included test!

But my test was still failing! The error was:
TypeError: window.matchMedia is not a function
The cause of this is that Jest uses JSDom to create a browser environment, which doesn't support window.matchMedia, and I was using this in my code. After some googling, it turns out that it could be mocked out. So I created a __mocks__/matchMedia.js file with the following contents:

  window.matchMedia = jest.fn().mockImplementation(query => {
    return {
      matches: false,
      media: query,
      onchange: null,
      addListener: jest.fn(),
      removeListener: jest.fn(),
    };
  });

In my __tests__/App.js file I imported this mock and low and behold a different error! This error was about having a store, I used the "redux-mock-store" npm package to mock out my redux store and wrapping my <App> in a <Provider>, and my __tests__/App.js file ended up looking like this:

  import React from 'react';
  import ReactDOM from 'react-dom';
  import '../__mocks__/matchMedia';
  import { Provider } from 'react-redux';
  import configureMockStore from 'redux-mock-store';
  import App from '../App';

  const mockStore = configureMockStore();
  const store = mockStore({});

  describe('App', () => {
    it('renders without crashing', () => {
      const div = document.createElement('div');
      ReactDOM.render((
        <Provider store={store}>
          <App />
        </Provider>
      ), div);
    });
  });

And guess what... It PASSED!

No comments:

Post a Comment