Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jesstelford/react-testing-mocha-jsdom
Unit testing React Components with Mocha + jsdom
https://github.com/jesstelford/react-testing-mocha-jsdom
Last synced: about 12 hours ago
JSON representation
Unit testing React Components with Mocha + jsdom
- Host: GitHub
- URL: https://github.com/jesstelford/react-testing-mocha-jsdom
- Owner: jesstelford
- Created: 2014-12-28T03:32:48.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2018-10-06T14:58:25.000Z (about 6 years ago)
- Last Synced: 2024-12-26T13:08:51.683Z (8 days ago)
- Language: JavaScript
- Size: 233 KB
- Stars: 263
- Watchers: 9
- Forks: 30
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
*This is __Part 2__ of the series* "Modular Isomorphic React JS applications".
*See [Part 1](https://github.com/jesstelford/react-isomorphic-boilerplate) and
[Part 3](https://github.com/jesstelford/react-testing-isomorphic) 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](https://facebook.github.io/react) is traditionally done
using [Jest](https://facebook.github.io/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](http://mochajs.org/) 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](http://chaijs.com/),
[better-assert](https://github.com/tj/better-assert), etc), and mocking
frameworks ([Sinon](http://sinonjs.org/), etc).[jsdom](https://github.com/tmpvar/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](https://github.com/jesstelford/react-testing-mocha-jsdom)We'll be using these libraries:
* [Node.js](http://nodejs.org)
* [npm](https://www.npmjs.org)
* [React](https://www.npmjs.com/package/react) - ^0.12.0
* [react-tools](https://www.npmjs.com/package/react-tools) - to compile JSX to JS
* [Mocha](http://mochajs.org/) - testing framework and runner
* [jsdom](https://github.com/tmpvar/jsdom) - headless DOM for React to use in testsOur 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](https://github.com/jesstelford/react-isomorphic-boilerplate#server-side-rendering):
```javascript
// 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 (
{this.props.name}
);
}
});
```### jsdom
Setting up jsdom ^2.0.0 can be acheived in a couple of lines:
```javascript
// 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('');
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(...)`](https://github.com/tmpvar/jsdom#for-the-hardcore-jsdomjsdom)
is a very simple document, and can be simplified further to `` if
you wish. I have kept the `...` 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](https://github.com/jesstelford/react-testing-mocha-jsdom/blob/master/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:```javascript
// file: test/component/todo-item.js
var assert = require('assert');describe('Todo-item component', function(){
it(' 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`](https://facebook.github.io/react/docs/test-utils.html) to allow
searching for such an element, the most useful for us being
[`findRenderedDOMComponentWithTag()`](https://facebook.github.io/react/docs/test-utils.html#findrendereddomcomponentwithtag).We can do this in one of Mocha's `before` methods, executed before all the
tests:```javascript
// 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(
);// Searching for 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):```javascript
// 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(
);// Searching for tag within rendered React component
// Throws an exception if not found
var inputComponent = TestUtils.findRenderedDOMComponentWithTag(
renderedComponent,
'input'
);this.inputElement = inputComponent.getDOMNode();
});it(' should be of type "checkbox"', function() {
assert(this.inputElement.getAttribute('type') === 'checkbox');
});it(' should not be checked', function() {
assert(this.inputElement.checked === false);
});
});
```## Results
```bash
$ npm test> [email protected] test /home/teddy/dev/react-mocha-jsdom
> mocha --recursiveTodo-item component
✓ should be of type "checkbox"1 passing (25ms)
```