Testing React components

Just like with utility modules, creating tests for React components starts with creating the __tests__ directory. Navigate to ~/snapterest/source/components/ and create the __tests__ directory.

The first React component that we'll test will be our Header component. Create Header-test.js in the ~/snapterest/source/components/__tests__ directory:

jest.dontMock('../Header.react'),

describe('Header component', function () {

  it('renders provided header text', function () {

    var React = require('react'),
    var ReactDOM = require('react-dom'),
    var TestUtils = require('react-addons-test-utils'),
    var Header = require('../Header.react'),

    var header = TestUtils.renderIntoDocument(
      <Header text="Testing..." />
    );

    var actualHeaderText = ReactDOM.findDOMNode(header).textContent;

    expect(actualHeaderText).toBe('Testing...'),

    var defaultHeader = TestUtils.renderIntoDocument(
      <Header />
    );

    var actualDefaultHeaderText = ReactDOM.findDOMNode(defaultHeader).textContent;

    expect(actualDefaultHeaderText).toBe('Default header'),
  });
});

By now, you can recognize the structure of our test files. First, we tell Jest not to mock the Header component. Then, we define our test suit, and we give it a name, 'Header component'. Our test suit has one spec named, renders provided header text. As the name suggests, it tests whether our Header component renders the provided header text. The implementation of that spec has a number of new things that we'll discuss. Let's take a closer look at them.

First, we import React and ReactDOM:

var React = require('react'),
var ReactDOM = require('react-dom'),

Then, we import one of the React add-ons:

var TestUtils = require('react-addons-test-utils'),

TestUtils helps you test the React components with any test framework that you choose. Naturally, it works great with Jest. Let's install it:

npm install --save-dev react-addons-test-utils

Next, in order to test our Header component, we need to import it:

var Header = require('../Header.react'),

Our next task is to render the Header component to the DOM. The TestUtils add-on has a helper renderIntoDocument() function that does exactly this:

var header = TestUtils.renderIntoDocument(
  <Header text="Testing..." />
);

We pass the React <Header text="Testing..." /> component instance to the renderIntoDocument() function as a parameter. Notice that in this case, our Header component instance has a text property. renderIntoDocument() returns a reference to that component.

Now, we have a reference to our Header component that is rendered to the DOM. Our next task is to check what header text did it render. This means that we need to perform the following steps:

  1. Find the component's DOM node.
  2. Get the DOM node's text content.

Do you remember what method React provides us to find the component's DOM node? Did you say ReactDOM.findDOMNode()?

ReactDOM.findDOMNode(header)

We pass header to ReactDOM.findDOMNode() as a parameter. As a result, ReactDOM.findDOMNode() returns a DOM node element. Now we can access its textContent property:

ReactDOM.findDOMNode(header).textContent;

The value of the textContent property becomes the actual header text:

var actualHeaderText = ReactDOM.findDOMNode(header).textContent;

The final step is to create an expectation and match it with the expected text:

expect(actualHeaderText).toBe('Testing...'),

As you can tell, we're expecting Testing... to be rendered as a DOM node text.

Great! Now we can test that the provided header text to the Header component instance is rendered to the DOM.

What happens when we create the Header component instance without providing any header text?

Let's find this out by rendering another instance of the Header component to the DOM:

var defaultHeader = TestUtils.renderIntoDocument(
  <Header />
);

Only this time, it has no text property. We'll call this component reference defaultHeader. Let's find the defaultHeader component's DOM node element and access its textContent property:

var actualDefaultHeaderText = ReactDOM.findDOMNode(defaultHeader).textContent;

This will be our actual header text rendered by default by a Header component. Finally, we create an expectation and match this with the expected text:

expect(actualDefaultHeaderText).toBe('Default header'),

In this case, actualDefaultHeaderText must be equal to Default header.

This is how you test what your React component renders. You might be wondering how do you test the behavior of your React component?

That's what we'll discuss next!

Create the Button-test.js file in the ~/snapterest/source/components/__tests__/ directory:

jest.dontMock('../Button.react'),

describe('Button component', function () {

  it('calls handler function on click', function () {

    var React = require('react'),
    var TestUtils = require('react-addons-test-utils'),
    var Button = require('../Button.react'),
    var handleClick = jest.genMockFunction();

    var button = TestUtils.renderIntoDocument(
      <Button handleClick={handleClick} />
    );

    var buttonInstance = TestUtils.findRenderedDOMComponentWithTag(button, 'button'),

    TestUtils.Simulate.click(buttonInstance);

    expect(handleClick).toBeCalled();

    var numberOfCallsMadeIntoMockFunction = handleClick.mock.calls.length;

    expect(numberOfCallsMadeIntoMockFunction).toBe(1);
  });
});

The Button-test.js file will test our Button component, and specifically, check whether it triggers the event handler function when you click on it. Without further ado, let's focus on the 'calls handler function on click' spec implementation:

var React = require('react/addons'),
var TestUtils = require('react-addons-test-utils'),
var Button = require('../Button.react'),
var handleClick = jest.genMockFunction();

var button = TestUtils.renderIntoDocument(
  <Button handleClick={handleClick} />
);

var buttonInstance = TestUtils.findRenderedDOMComponentWithTag(button, 'button'),

TestUtils.Simulate.click(buttonInstance);

expect(handleClick).toBeCalled();

var numberOfCallsMadeIntoMockFunction = handleClick.mock.calls.length;

expect(numberOfCallsMadeIntoMockFunction).toBe(1);

First, we're importing React with the add-ons module and our Button component. As usual, we tell Jest not to mock the Button component.

Before we continue with implementing our spec, let's talk about how we're going to implement it. The plan is to:

  1. Generate a mock function.
  2. Render the Button component instance with our mock function that imitates a click handler.
  3. Find the instance of our rendered Button component.
  4. Simulate a click event on that component instance.
  5. Check whether a click event handler function was triggered.
  6. Check whether our mock function was called exactly once.

Let's generate a mock function:

var handleClick = jest.genMockFunction();

The jest.genMockFunction() function returns the newly generated Jest mock function ; we name it handleClick.

Next, we render the instance of our Button component to the DOM:

var button = TestUtils.renderIntoDocument(
  <Button handleClick={handleClick} />
);

This Button component instance receives our mock handleClick function as a property.

Then, we find the Button component instance rendered to the DOM:

var buttonInstance = TestUtils.findRenderedDOMComponentWithTag(button, 'button'),

The TestUtils.findRenderedDOMComponentWithTag() method finds one instance of the Button component that is rendered as a button tag. We store that instance in the buttonInstance variable.

Next, we simulate a click on that component instance:

TestUtils.Simulate.click(buttonInstance);

TestUtils.Simulate simulates an event dispatch on a DOM node. For our purposes, we need to simulate a click event dispatch. For this, we need to call the TestUtils.Simulate.click() method and pass buttonInstance as a DOM node element. TestUtils.Simulate provides an event method such as click() for all events supported by React.

Finally, we create an expectation:

expect(handleClick).toBeCalled();

As you might have guessed, we expect our handleClick mock function to be called at least once during our test.

What we also want to check is whether it's called exactly once. How do we check the number of calls made into our handleClick mock function? All the Jest mock functions have a special .mock property that stores all the data about how the mock function was called. We'll take a look at our handleClick mock function's .mock property to find out how many times handleClick was called:

var numberOfCallsMadeIntoMockFunction = handleClick.mock.calls.length;

The .mock property has the .calls property, which is an array, that represents all the calls that have been made into our handleClick mock function. The length of that array will be the number of calls made into handleClick. We store this number in the numberOfCallsMadeIntoMockFunction variable and then create another expectation:

expect(numberOfCallsMadeIntoMockFunction).toBe(1);

We expect the number of calls made into our mock function to be exactly 1.

Right now, we've finished creating our tests. However, we can't run them yet; can you guess why? Let's think about it. We're testing the React components written in the JSX syntax, but Jest doesn't understand the JSX syntax. So, if you run npm test now, both the component tests will fail and report SyntaxError.

What we need to do is to configure Jest in order to use a preprocessor provided by the babel-jest module. Add the jest property to your ~/snapterest/package.json file:

"jest": {
  "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
  "testFileExtensions": ["es6", "js"],
  "unmockedModulePathPatterns": [
    "<rootDir>/node_modules/react"   
  ]
}

Install babel-jest:

npm install --save-dev babel-jest

If you're curious about what unmockedModulePathPatterns does, then go to https://facebook.github.io/jest/docs/api.html#config-unmockedmodulepathpatterns-array-string.

Now it's time to run all our tests.

Navigate to ~/snapterest/ and run this command:

npm test

All your test suits should PASS:

PASS  source/utils/__tests__/TweetUtils-test.js (0.134s)
PASS  source/utils/__tests__/CollectionUtils-test.js (0.146s)
PASS  source/components/__tests__/Button-test.js (2.802s)
PASS  source/components/__tests__/Header-test.js (2.877s)
4 tests passed (4 total)
Run time: 4.728s

Log messages, such as these, will help you sleep well at night and go on holidays, without the need to constantly check your work e-mails.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.234.225