Our journey through React unit testing

Vincent Voyer

@zeroload
github.com/vvo
algolia 
💻
react newbie

Goal

expect(testing).toBe('easy')

The boring bullet point list

  • Context
  • Unit testing?
  • Unit testing in React community
  • Available solutions
  • Shallow rendering
  • Tooling
  • Going further
  • Questions
  • ​This bullet intentionally left useless

Context

instantsearch.js

A library of widgets to build high performance instant search experiences using Algolia

instantsearch.js

Based on React

instantsearch.js

Unit testing

basics

Test single units

Unit testing is not enough

—A Selenium user

Unit testing = fun

Testing units ≠ The application is working 

You still need Selenium

AKA e2e, functional integration testing 

It's not unit testing 🆚 functional testing 

You need both

State of unit testing

in the React community

Why?

React unit testing

Available solutions

Real dom testing

import React from 'react';
import {findDOMNode} from 'react-dom';
import {renderIntoDocument} from 'react-addons-test-utils';
import jsdom from 'mocha-jsdom';
import expect from 'expect';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div><Label name={this.props.name} /></div>;
  }
}

describe('Real dom test', () => {
  jsdom({useEach: true});

  it('works', () => {
    let component = renderIntoDocument(<Button name="John" />);
    let DOMNode = findDOMNode(component);
    expect(
      DOMNode.querySelector('span')
        .textContent
    ).toEqual('Hello John');
  });
});

+ Not bound to any specific tool
+ You can use jQuery 
- Not really unit testing
- Renders the full tree

import React from 'react';
import {
  renderIntoDocument,
  findRenderedDOMComponentWithTag
} from 'react-addons-test-utils';
import jsdom from 'mocha-jsdom';
import expect from 'expect';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div><Label name={this.props.name} /></div>;
  }
}

describe('Real Test Utilities', () => {
  jsdom({useEach: true});

  it('works', () => {
    let component = renderIntoDocument(<Button name="John" />);
    expect(
      findRenderedDOMComponentWithTag(component, 'span')
        .textContent
    ).toEqual('Hello John');
  });
});

+ Test .props, refs, key
- Not really unit testing
- Renders the full tree
- Lots of assertions to do

import React from 'react';
import expect from 'expect';
import {createRenderer} from 'react-addons-test-utils';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div><Label name={this.props.name} /></div>;
  }
}

describe('Shallow rendering', () => {
  it('works', () => {
    let renderer = createRenderer();
    renderer.render(<Button name="John" />);
    let actualElement = renderer.getRenderOutput();
    let expectedElement = <div><Label name="John" /></div>;
    expect(actualElement).toEqual(expectedElement);
  });
});

+ Unit!
+ render() one level deep
- Some limitations:
  - no refs
  - no TestUtils.simulate

Testing on* handlers

import React from 'react';
import expect from 'expect';
import {createRenderer} from 'react-addons-test-utils';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div onClick={this.props.click}><Label name={this.props.name} /></div>;
  }
}

describe('Shallow rendering on* handlers', () => {
  it('works', () => {
    let renderer = createRenderer();
    let hasClicked = false;
    let click = () => hasClicked = true;
    renderer.render(<Button name="John" click={click} />);
    renderer.getRenderOutput().props.onClick();
    expect(hasClicked).toBe(true);
  });
});

Tooling

__tests__

without

with

+ mocha
+ jsdom
+ expect(-jsx)
= npm run test:watch

algolia/react-unit-test-talk

What do we use and recommend

  • Shallow rendering
  • Regular test utilities and jsdom when needed
  • mocha + jsdom + expect + expect-jsx + babel-plugin-rewire

Going further

  • shallow rendering + refs?
  • shallow rendering + simulate?
  • testing state?
  • Unit tests using real browsers
  • E2E using real browsers

End

Questions?

Sources