• Stars
    star
    263
  • Rank 151,940 (Top 4 %)
  • Language
    JavaScript
  • Created over 9 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Unit testing React Components with Mocha + jsdom

This is Part 2 of the series "Modular Isomorphic React JS applications". See Part 1 and Part 3 for more.

Unit testing React Components with Mocha + jsdom

tl;dr: Jest can be replaced with Mocha + jsdom for unit testing headlessly in node/io.js, keeping open the option for future tooling changes. Modularity FTW!

Unit testing React is traditionally done using Jest which dictates the use of the Jasmine testing framework, and enforces mocking of all require calls. For such an unopinionated rendering engine, this is a very opinionated setup, resulting in issues such as overly-verbose unmocking of requires, and being tied into Jasmine's limited framework.

The Mocha testing framework is a simple, fast, and async-friendly testing framework with its own test runner built in. We can choose our own Assertion handlers (Chai, better-assert, etc), and mocking frameworks (Sinon, etc).

jsdom provides React the required DOM to render to, implementing a suitable subset of browser's DOM implementations entirely in node/io.js.

With these three tools combined, unit testing react components becomes easier, and with less vendor-lock for future changes (we can swap out jsdom for some other implementation in the future, etc).

Let's do it

tl;dr: Get the completed example

We'll be using these libraries:

Our code structure will look like this:

├── common
│   └── components  # All our react components
└── test
    └── components  # Unit tests for components

todo-item.js React component

We previously built the component common/components/todo-item.js in Part 1:

// file: common/components/todo-item.js
var React = require('react');

module.exports = React.createClass({
  displayName: 'TodoItem',

  getInitialState: function() {
    return { done: this.props.done }
  },

  render: function() {
    return (
      <label>
        <input type="checkbox" defaultChecked={this.state.done} />
        {this.props.name}
      </label>
    );
  }
});

jsdom

Setting up jsdom ^2.0.0 can be acheived in a couple of lines:

// file: test/setup.js
var jsdom = require('jsdom');

// A super simple DOM ready for React to render into
// Store this DOM and the window in global scope ready for React to access
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = document.parentWindow;

We are emulating a browser environment here by setting the global variables document and window as created by jsdom. This later allows React to render into the document, and utilize functions existing on window such as onScroll.

The HTML passed into jsdom.jsdom(...) is a very simple document, and can be simplified further to <body></body> if you wish. I have kept the <!doctype html><html>...</html> tags to future proof against a time when jsdom can distinguish between HTML5 and older DOM models.

A Mocha Test

tl;dr: Get the completed test file in the example repo at test/component/todo-item.js

Let's begin with scaffolding our tests, to figure out what we are aiming for in terms of test setup and teardown:

// file: test/component/todo-item.js
var assert = require('assert');

describe('Todo-item component', function(){

  it('<input> should be of type "checkbox"', function() {
    assert(this.inputElement.getAttribute('type') === 'checkbox');
  });

});

First up, how do we get access to this.inputElement? React provides a nice set of TestUtils to allow searching for such an element, the most useful for us being findRenderedDOMComponentWithTag().

We can do this in one of Mocha's before methods, executed before all the tests:

// file: test/component/todo-item.js
var assert = require('assert');

describe('Todo-item component', function(){

  before('render and locate element', function() {
    var renderedComponent = TestUtils.renderIntoDocument(
      <TodoItem done={false} name="Write Tutorial"/>
    );

    // Searching for <input> tag within rendered React component
    // Throws an exception if not found
    var inputComponent = TestUtils.findRenderedDOMComponentWithTag(
      renderedComponent,
      'input'
    );

    this.inputElement = inputComponent.getDOMNode();
  });

  it( /* [...] */ )

});

Now, we have extracted the DOM Node (this.inputElement) from the rendered React component (renderedComponent) using React's getDOMNode() method.

renderIntoDocument is where jsdom comes in, allowing us to access the document object as if we were in a browser!

All together now, and we end up with a complete test that can be run with ./node_modules/.bin/mocha --compilers js:babel/register --recursive (alternatively can be run as npm test in the example repo):

// file: test/component/todo-item.js
var React = require('react/addons'),
    assert = require('assert'),
    TodoItem = require('../../common/components/todo-item'),
    TestUtils = React.addons.TestUtils;

describe('Todo-item component', function(){
  before('render and locate element', function() {
    var renderedComponent = TestUtils.renderIntoDocument(
      <TodoItem done={false} name="Write Tutorial"/>
    );

    // Searching for <input> tag within rendered React component
    // Throws an exception if not found
    var inputComponent = TestUtils.findRenderedDOMComponentWithTag(
      renderedComponent,
      'input'
    );

    this.inputElement = inputComponent.getDOMNode();
  });

  it('<input> should be of type "checkbox"', function() {
    assert(this.inputElement.getAttribute('type') === 'checkbox');
  });

  it('<input> should not be checked', function() {
    assert(this.inputElement.checked === false);
  });
});

Results

$ npm test

> [email protected] test /home/teddy/dev/react-mocha-jsdom
> mocha --recursive



  Todo-item component
    ✓ <input> should be of type "checkbox"


  1 passing (25ms)

More Repositories

1

react-bsod

🔵 A Blue Screen Of Death (bsod) component to display your errors.
JavaScript
320
star
2

react-workshop

An at-your-own pace practical workshop for absolute beginners to react
252
star
3

react-isomorphic-boilerplate

Isomorphic Server & Browser Side Rendering with React >= 0.12
JavaScript
165
star
4

aframe-click-drag-component

Aframe Click & Drag Component
JavaScript
96
star
5

extract-to-react

Chrome/Chromium extension for easy HTML to React conversion.
JavaScript
61
star
6

color-svg-sprite

A technique for coloring external .svg files using external .css files.
HTML
59
star
7

aframe-video-billboard

Video Billboard entity & component for A-Frame.
JavaScript
39
star
8

node-MarkerWithLabel

npm module of Google Map utility's Marker With Label
JavaScript
39
star
9

esnext-generation

Learn ES6's Iterators & Generators in this interactive Javascript workshop.
JavaScript
38
star
10

react-testing-isomorphic

Testing Isomorphic React components
JavaScript
37
star
11

pecs

A PICO-8 Entity Component System (ECS) library
Lua
36
star
12

aframe-map

Map entity & component for A-Frame.
JavaScript
35
star
13

birdview-chrome-extension

Get a glance at a whole web page with an aerial view
JavaScript
31
star
14

jsonschemaphp

JSON Schema PHP Validator
PHP
27
star
15

wd15

Web Directions 2015
26
star
16

next-keystone-app

Static Sites with Next.js + KeystoneJS
JavaScript
14
star
17

payment-channels

Bitcoin Payment Channels built on BitPay's libraries for Angel Hack 2014
CoffeeScript
12
star
18

reflux-triggerable-mixin

Reflux stores mixin adding `triggerable` syntax similar to `listenable`.
JavaScript
8
star
19

aframe-frustum-lock-component

Lock an entity to the camera frustum in A-Frame.
JavaScript
8
star
20

emoji-tetra-animated

Generates a gif of the MMO Tetra game hosted by https://twitter.com/EmjoiTetra
JavaScript
7
star
21

cloverfield-build-make

Example using Makefile for cloverfield
JavaScript
7
star
22

wd15-responsive-design

Notes from Vitaly Friedman's Responsive Web Design workshop at Web Directions Sydney 2015
6
star
23

version-changelog

Add a version & URL to your changelog
JavaScript
6
star
24

js-go-presentation

JSConfAU 2016 presentation: How to build Pokemon GO in 100% JS
JavaScript
5
star
25

playcanvas-offline-scripts-server

Enable offline script editing with PlayCanvas Scripts 2.0
JavaScript
5
star
26

relaxql

Relay + GraphQL = RelaxQL: A non-leaky nested component query library
JavaScript
4
star
27

pico8-boom-tower

Pico8 infinite tower game
Lua
3
star
28

changelog-verify

Verify a changelog has correct entries
JavaScript
3
star
29

js-go

AR game in JS
JavaScript
3
star
30

react-checkem

Simple "Select All" for checkboxes in React
JavaScript
3
star
31

node-smallxhr

Very Small XHR library for easy, minimal cross-browser requests
JavaScript
3
star
32

OAuth-PHP

OAuth - Consumer and Server library for PHP
PHP
2
star
33

krapstack.com

KRAP: Keystone + React + APollo
HTML
2
star
34

falcor-shapes-prop-types

Conversion from falcor-shapes to React propTypes
JavaScript
2
star
35

node-encode-decode

A Javascript object to encode and/or decode html characters using HTML or Numeric entities that handles double or partial encoding.
JavaScript
2
star
36

coffee-boilerplate

A quickstart CoffeeScript node server, designed to serve compiled, minified, and source-mapped CoffeeScript modules to the browser.
CoffeeScript
2
star
37

mustache-cache

Compile mustache templates into a requirable, callable function
JavaScript
1
star
38

pico-8-motion-blur

Lua
1
star
39

pico-8-starter

Basic starter template for PICO-8 projects
Lua
1
star
40

random

lambda API to generate a random number from 0-1
JavaScript
1
star
41

pico-8-confetti

Lua
1
star
42

react-list-provider

Use Context to save lists of whatever you want, then access them anywhere in your componpent tree.
JavaScript
1
star
43

ga-js1-debugging

General Assembly JS1 Sydney - Lesson 14 - Debugging Exercise
JavaScript
1
star
44

firebase-workshop

Firebase v3 CRUD Workshop
JavaScript
1
star
45

sirds-dbpro

SIRDs - Single Image Random Dot Stereogram DLL
C++
1
star
46

slides-intro-to-js

Introduction To JS slide deck
JavaScript
1
star
47

scripts

Scripts to make new projects easier
JavaScript
1
star
48

doubledispatch

A mini tutorial on Double Dispatch (aka: Visitor Pattern)
PHP
1
star
49

keystone-next-adminui

A prototype of using Next.js as KeystoneJS's Admin UI
JavaScript
1
star
50

js13k-2016

js13k 2016 game competition entry
JavaScript
1
star
51

keystone-polymorphism-example

Example of creating polymorphic results from a Keystone API by leveraging GraphQL Union types.
JavaScript
1
star
52

jes.st

Personal blog
JavaScript
1
star
53

underdb

TypeScript
1
star
54

meetup-attendance

Meetup Attendance taking app
JavaScript
1
star
55

dotfiles

My dotfiles
Shell
1
star
56

unix-tips

Tips & Tricks for using unix
1
star
57

gesture-recognition-dbpro

Discrete Handwriting (Gesture) Recognition
1
star
58

wallet-sweep

Dogecoin Paper Wallet sweeping made easy via cross platform QR code scanning in the browser.
CoffeeScript
1
star
59

ultimateteamtbc

Site for handling team communication
JavaScript
1
star
60

talk-bet-on-react

My talk "Why I bet my business on React" for NDC Sydney 2018
JavaScript
1
star
61

ga-js1-spa

JS1 Project 3: SPA
CSS
1
star