More React unit testing with Jest and Enzyme

I’ve been writing some additional tests to get a better idea of how to use Jest and Enzyme for testing my React components (see my first attempts here).

I have a simple Flux app that had a LabelComponent and a ButtonComponent. The full source is here. I want to test the render output is what I expect. The render method on my LabelComponent is pretty simple:

render() {
    return (
        <div className="label">
            Text: { this.state.labelValue.value }
        </div>
    );
}

This component has state which comes from a Store, so this seems a little more interesting to test.

Using Enzyme you can either fully render a component to a virtual DOM using mount() and then check the results, or you can shallow() render which only renders that component and not any additional child dependencies (this is probably more useful for unit tests).

Using mount(), you can check for expected rendered output in the resulting DOM using .contains() – without doing anything to initialize the Store, this is my first step:

import React from 'react';
import {shallow, mount} from 'enzyme';
import LabelComponent from '../components/LabelComponent';
it('renders with text value', () => {

    const result = mount(<LabelComponent/>);
    expect(result.contains(<div className="label">Text: </div>));
});

This is good so far, but I’m really more interested in how the component renders when the Store contains values to be rendered. I’m not sure if I’ve found the right way to do this, but looking for info on how to mock out other parts of my app, like the Store, took me down a rabbit hole. The Jest docs for mocking are not particularly useful. In the api reference there’s something that looks like it allows you to setup a mock return from a function, but it doesn’t have enough info to tell you exactly how to use it.

It seems the Jest api has changed enough over time that it’s hard to find any articles that are current, and show you how the current api should be used. This SO post has a question that’s pretty close to what I was trying to do. There’s two answers that combined together got me to a working solution.

This approach didn’t work for me:

const getData = jest.fn().mockImplementation(() => {
    return {
        getData: jest.fn().mockReturnValue({'labelValue' : { 'value' : 'test'} } )
    }
});

… but, this approach did exactly what I needed:

jest.mock('../stores/LabelStore');
import LabelStore from '../stores/LabelStore';
const getData = jest.fn().mockImplementation(function() {
    return {'labelValue' : { 'value' : 'testvalue'} }
    }
);
LabelStore.getData = getData.bind(LabelStore);

As far as I work out, this is mocking my getData() function to return the mocked data I want for testing, and then binding it to a mocked LabelStore.

I’m not sure if this is the best approach for mocking with Jest, but this is doing exactly what I need. If I learn more about this approach I’ll come back and provide an update later.

Unit testing React components with Jest

Jest is a unit test framework for testing React apps. The Getting Started guide is pretty much all you need to get started. If you’ve created your project using create-react-app then you’re already setup, just run ‘npm test’ and a runner will test up that repeatedly runs your tests as you make code or test changes.

create-react-app also creates a sample test for the sample App.js component. This is a good starting point to follow for other tests:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

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

To test the results of rendering a component, the enzyme library allows you to easily capture the output from a render, and then query the results. For example:

import React from 'react';
import ReactDOM from 'react-dom';
import {shallow, mount} from 'enzyme';
import Calculator from '../Components/CalculatorComponent';
it('renders with result 3', () => {
    const result = mount(<Calculator value1="1" value2="2" />);
    result.find('button').simulate('click');
    const buttonResult = result.find("#result").first();
    expect(buttonResult.text()).toEqual("3");
});

The CalculatorComponent I’m testing here is the one from my previous React post.

WebStorm: adding library support for Jasmine

WebStorm provides support to download libraries to provide code complete for a huge number of popular libraries.

I tried to add a Library for Jasmine manually, but really wasn’t sure where to point to, I tried pointing to here, but this didn’t seem to work for me:

/usr/local/lib/node_modules/karma-jasmine

If you press the Download button on the right, you can search for a known library and install it like this:

I think part of what I was looking for was jasmine and karma-jasmine, but installing from the Download option got these setup for me, and now I’ve got the code complete in my Jasmine tests that I was looking for.

Webstorm: Adding karma.conf.js to an existing Javascript project

To setup an existing HTML/Javascript project in Webstorm to use Jasmine and Karma:

  • click the Terminal tab in the bottom left
  • Enter: karma init karma.conf.js
  • When prompted, answer the setup questions:
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
> 

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> js/*.js
> test/**/*Spec.js
WARN [init]: There is no file matching this pattern.

> 

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
> 

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

Next, right-click the karma.conf.js file in the Project files area, and select ‘Create karma.conf.js’ – the dialog should point to your local karma and node.js installs, select the defaults.

Run the file and Karma starts up. Good to go!