Hasan Setiawan

Write, write, write give your wings on code!

Follow me on GitHub

React Unit Test Using Mocha

Heres the dependencies you should install for a solid test set up:

            
npm i babel-core babel-preset-es2015 babel-preset-react babel-preset-stage-2
npm i chai mocha enzyme sinon
npm i react react-addons-test-utils react-dom
n.b. react-addons-test-utils and react-dom are implicit dependencies of react.

Then you’ll need to pass babel-core/register to mocha, so in your test script:

    
"test": "mocha -w --compilers js:babel-core/register '[glob to match test files]'"
// a glob might be something like: **/*.spec.js
// this would match any file whose names ends with .spec.js in any directory
You’ll need to declare your babel plugins, which you can do in your package.json or a .babelrc.

// .babelrc
{
  "presets": ["es2015", "react", "stage-2"],
  "ignore": "node_modules"
}

// package.json
"babel": {
  {
    "presets": ["es2015", "react", "stage-2"],
    "ignore": "node_modules"
  }
}

some components to test Lets add two simple components. One is a save button and the other rendered a list of names passed through props as an array. By no means a perfect component, the myComponent component will demonstrate how we can test state, rendering and click events/even handlers.

    
saveButton.js
import React from 'react';

export default (props) => {
  return ();
}
myComponent.js
import React from 'react';
import Save from './saveButton';

export default React.createClass({
  getInitialState() {
    return {
      title: 'list'
    }
  },
  selected(evt) {
    this.setState({
      title: `you selected ${evt.target.innerText}`
    })
  },
  renderListItem(name) {
    return (
    
    );
  },
  renderList() {
    if (this.props.names) {
      return (
      
      );
    }
    return null;
  },
  render() {
    return (
    
    );
  }
});

myComponent.spec.js Heres out test file

    
import React from 'react';
import { shallow, mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';

import MyComponent from './myComponent';
import SaveButton from './saveButton';


describe('', () => {
  describe('when rendered', () => {
   let component, sandbox, eventHandler;

    beforeEach(() => {
      sandbox = sinon.sandbox.create();
      eventHandler = sandbox.stub();
      component = shallow();
    });

    it('should have a  as it\'s root elment', () => {
      expect(component.is('div')).to.equal(true);
    });

    it('should have a save button', () => {
      expect(component.containsMatchingElement(SomeButton)).to.equal(true);
    });

    describe('when the save button is clicked', () => {
      it('should call any callback functionality', () => {
        sinon.assert.notCalled(eventHandler);
        component.find(SomeButton).simulate('click');
        sinon.assert.calledOnce(eventHandler);
      });
    });
});

describe('when given a list of names', () => {
    let component, items, title;
    let names = ['Andrew', 'Bob', 'Steve', 'Colin'];

    beforeEach(() => {
      component = shallow();
      items = component.find('li');
      title = component.find('h3');
    });

    it('should show a unordered list containing those names', () => {
      expect(items).to.have.length(names.length);

      items.forEach((item, index) => {
        expect(item.text()).to.equal(names[index]);
      });
    });

    describe('the lists title', () => {
      it('should initially say \'list\'', () => {
        expect(title.text()).to.equal('list');
      });

      describe('when a list item has been clicked', () => {
        let item;

        beforeEach(() => {
          item = component.find('li').first();
          item.simulate('click', {target: {innerText: item.text()}});
          title = component.find('h3');
        });

        it('should update the title with the item clicked', () => {
          expect(title.text()).to.equal('you selected Andrew');
        });
      });
    });
  });
});

To test our code we run the test script: npm test If everything is working correctly you’ll see 6 passing tests and a warning about adding keys to iterated output (i.e. when we map over a function that returns a component we should also add a unique key).