Workflow and strategies
Vladimir Novick
Front End Developer & Architect
mail: vnovick@gmail.com
twitter: @VladimirNovick
github: vnovick
facebook: vnovickdev
Eliminate fear of change
var webpackConfig = require('./frontend/webpack.config.js');
var path = require("path");
module.exports = function(config) {
config.set({
basePath: __dirname,
resolve: {
root: [
path.join(__dirname, 'frontend'),
],
},
frameworks: ['mocha-debug', 'mocha'],
files: [
'frontend/scripts/specs/test.js'
],
exclude: [
],
plugins: [
'karma-chrome-launcher',
'karma-mocha',
'karma-sourcemap-loader',
'karma-webpack',
'karma-mocha-debug'
],
preprocessors: {
'frontend/scripts/specs/test.js': [ 'webpack', 'sourcemap' ]
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
webpack: webpackConfig
});
};
Advised by Rackt team: https://github.com/mjackson/expect
import React from 'react';
import expect from 'expect';
import expectJSX from 'expect-jsx';
expect.extend(expectJSX);
class TestComponent extends React.Component {}
describe('expect-jsx', () => {
it('works', () => {
expect(<div />).toEqualJSX(<div />);
// ok
expect(<div a="1" b="2" />).toEqualJSX(<div />);
// Error: Expected '<div\n a="1"\n b="2"\n/>' to equal '<div />'
expect(<span />).toNotEqualJSX(<div/>);
// ok
expect(<div><TestComponent /></div>).toIncludeJSX(<TestComponent />);
// ok
});
});
is extended with expect-jsx
There are different techniques to test React Component with react-addons-test-utils
Provides set of useful functions to check if React elements are rendered correctly
import { renderIntoDocument, isCompositeComponentWithType, isElementOfType } from 'react-addons-test-utils';
import { assert } from 'chai';
var HelloMessage = React.createClass({
render: function() {
return <div><Title text={this.props.name}/></div>;
}
});
const Title = ({ text }) => (<h1 className="title">{text}</h1>)
describe('Hello Message', () => {
it('Hello Message renders as composite component of type HelloMessage, () => {
const HelloComponent = renderIntoDocument(<HelloMessage name="testName"/>);
assert.equal(isCompositeComponentWithType(HelloComponent, HelloMessage), true)
});
it('Hello Message instance is created properly, () => {
assert.equal(isElementOfType(<HelloMessage/>, HelloMessage), true)
});
});
renderIntoDocument
mockComponent
isElement
isElementOfType
isCompositeComponent
scryRenderedDOMComponentsWithClass
findRenderedDOMComponentWithClass
React test utils provide Simulate method to simulate every event React understands
import { Simulate, findRenderedDOMComponentWithClass, renderIntoDocument } from 'react-addons-test-utils'
let HelloMessage = React.createClass({
render: function() {
return <div>
<Title clickCallback={()=>{ console.log("Click")}}/>
</div>;
}
});
let Title = ({clickCallback}) => <div className="titleComponent"
onClick={clickCallback}>
Title with click callback
</div>
let helloComponent = renderIntoDocument(<HelloMessage/>);
Simulate.click(
findRenderedDOMComponentWithClass(helloComponent, 'titleComponent')
);
Shallow rendering is our current recommendation.
It allows you to instantiate a component and get the result of its render function (React Element object).
At that point you can inspect its type and props to verify that the correct result was rendered
Ben Alpert - React team
Shallow Renderer will render only one level deep meaning you don't need to worry about behavior of child components
import { createRenderer } from 'react-addons-test-utils';
import { assert } from 'chai';
var HelloMessage = React.createClass({
render: function() {
return <div><Title text={this.props.name}/></div>;
}
});
const Title = ({ text }) => (<h1 className="title">{text}</h1>)
describe('Hello Message - check for children type', () => {
it('Hello Message renders children of type Title, () => {
const shallowRenderer = createRenderer();
shallowRenderer.render(<HelloMessage name="Vladimir"/>);
assert.equal(
isElementOfType(
shallowRenderer.getRenderOutput().props.children.type,
Title
),
true);
});
});
skin-deep wraps shallowRender with some useful extras to access instance methods
import React from 'react';
import sd from 'skin-deep';
const tree = sd.shallowRender(React.createElement(MyComponent, {}));
const instance = tree.getMountedInstance();
const vdom = tree.getRenderOutput();
import React from 'react';
import sd from 'skin-deep';
let HelloMessage = ({ name }) =>
<div>
<Title text={name}/>
<Title2/>
</div>;
let Title = ({ text }) => <h1 className="title">{text}</h1>
let Title2 = _ => <div>Dummy Test</div>;
let children = [
<Title text="Vladimir"/>,
<Title2/>
]
describe('Hello Message - check for children type', () => {
it('Hello Message renders children of type Title, () => {
const tree = sd.shallowRender(<HelloMessage name="Vladimir"/>);
const vdom = tree.getRenderOutput();
expect(vdom.props.children).to.deep.equal(children);
});
});
Skin deep wraps gives us .getMountedInstance method which lets us access instance methods and test them in isolation
import axios from 'axios';
import ChildComponent from 'childComponent';
export default class MyFancyWrapperComponent extends React.Component {
componentWillMount(){
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (response) {
console.log(response);
});
}
render() {
return (<div className="wrapper-style">
<ChildComponent {...this.props} />
</div>);
}
}
Problem:
Inner module dependencies are private for a module and cannot be accessed via test
A Babel plugin that adds the ability to rewire module dependencies.
{test: /src\/js\/.+\.js$/, loader: 'babel-loader?plugins=rewire' }
Webpack setup
rewire plugin is based on rewire.js but is itegrated into babel as transformer to support ES6 module syntax
import ComponentToTest from 'my-fancy-wrapper-component-module';
function mockAjax () {
//...
return new Promise(...);
}
ComponentToTest.__Rewire__('axios', mockAjax );
//....Assertion is here
ComponentToTest.__ResetDependency__('axios');
Also supports top level variables and require('myModule') overriding in the same manner
You can rewire module dependencies without importing a module
import { __RewireAPI__ as componentRewireAPI } from 'my-fancy-wrapper-component-module';
function mockAjax () {
//...
return new Promise(...);
}
componentRewireAPI.__Rewire__('axios', mockAjax );
//....Assertion is here
componentRewireAPI.__ResetDependency__('axios');
Almost everything is pure functions so the testing is even simpler. Less inner dependencies
react@90min.com