This example uses Webpack, but won't work if you're using Jest - have a look here for mocking ES6 imports with Jest.
Mocking imports in React components is important to be able to test them properly, although it can be tricky when running through Webpack and using ES6-style imports (import blah from MyClass
). I spent a while struggling to find a dependency injector which would work with the project I'm working on - I tried rewire, proxyquire and babel-plugin-rewire, but none of them worked how I wanted. I ended up using inject-loader, which allows me to mock imports in ES6 modules and test that they're being used correctly.
Install the module with npm install --save-dev inject-loader
, then take a look at the following example component and test.
Example React component (uses Redux)
import React from 'react';
import { connect } from 'react-redux'; // redux, not really important in this case
import { browserHistory } from 'react-router'; // the thing we'll be testing later
import MyLovelyComponent from 'components/MyLovelyComponent';
// Note that in this case, the component isn't the default export. You'll need to use the name
// of the export in your test, so in this case it'll be 'HomeView', but if this was 'export default'
// then the name in the test would be 'default'.
export class HomeView extends React.Component {
constructor () {
super();
}
// set up a click handler
handleClick (url) {
browserHistory.push(`/anotherpage/${url}`);
}
render () {
return (
<MyLovelyComponent onClick={ this.handleClick } />
);
}
}
// Redux: connect everything. This isn't important for this demo, but
// just note that this is the default export
export default connect(selector, actions)(HomeView);
Example test
import React from 'react';
// enzyme is a React test lib which makes it easier to find and query elements
// when testing, a bit like jQuery https://github.com/airbnb/enzyme
import { shallow } from 'enzyme';
// sinon allows you to stub and mock functions and objects, then check
// if methods were called on them
import sinon from 'sinon';
describe('(View) Home', function () {
beforeEach(function () {
let _component, browserStub;
// create a spy; we'll replace browserHistory with this
// then check that it was called
browserStub = sinon.spy();
// magic bit! Require inject loader, and pass in the component
// you want to test. There is syntax to allow or block modules in
// the component, see https://www.npmjs.com/package/inject-loader
const inject = require('inject-loader!views/HomeView');
// Note: with Webpack 1 you need to require 'inject' not 'inject-loader', ie
// const inject = require('inject!views/HomeView');
// now we can inject dependencies!
// HomeView requires browserHistory from react-router,
// so pass an object representing that
// we're only using the .push() method, so add our spy to
// the browserHistory stub
let HomeView = inject({
'react-router': {
browserHistory: {
push: browserStub
}
}
}).HomeView; // very important! This is the name of the export from your component.
// if we used 'default' we'd get the
// 'export default connect(selector, actions)(HomeView);' line from HomeView
// not our component. If you don't specify '.default' or '.HomeView' etc, then
// you'll just have an object listing all exports from your module, which
// won't work in the test
// shallow render the component. This means no child components are rendered,
// so we don't need to worry about stubbing them
_component = shallow(<HomeView />);
});
it('should handle click', () => {
// get the instance of our component and call the handleClick method.
// our spy 'browserStub' should be called with the argument passed to
// handleClick (see http://sinonjs.org/docs/)
_component.instance().handleClick('mypage');
expect(browserStub).to.have.been.calledWith('/anotherpage/mypage');
});
// you might also want to render the component and check
// that clicking MyLovelyComponent triggers handleClick etc
});