skynyrd
2/8/2017 - 8:52 AM

Testing React & Redux app w templates using Enzyme, Mocha and Expect

Testing React & Redux app w templates using Enzyme, Mocha and Expect

##### Integration Testing for Store
import expect from 'expect';
import {createStore} from 'redux';
import rootReducer from '../reducers';
import intitialState from '../reducers/initialState';
import * as fooActions from '../actions/fooActions';

describe('Store integration test', () => {
  it('should handle the action', () => {
    //arrange
    const store = createStore(rootReducer, initialState);
    const foo = { bar: "a" };
    
    //act
    const action = fooActions.createFooSuccess(foo);
    store.dispatch(action);
    
    //assert
    const actual = store.getState().foo;
    
    expect(actual).toEqual(foo);
  });
});
// JS, React, Redux ES6, Testing, Enzyme, Mocha, Template

import expect from 'expect';
import React from 'react';
import {mount, shallow} from 'enzyme';
import SampleConnectedComponentForm from './SampleConnectedComponentForm';

describe('Testing connected components', () => {
	it('sets error message when trying to save empty title', ()=>{
		// There are two ways to render connected components:
		// 1. Wrap the component with Provider:
		// const wrapper = mount(<Provider store={store}><SampleConnectedComponentForm/></Provider>);
		// 2. Exporting connected component itself besides exporting connected wrap.
		// simply add export near class definition, there should be two exports for one class, assume we have that:

		// Mock mapStateToProps and mapDispatchToProps
		const props = {
			arrPropFromStore: [],
			objPropFromStore : {},
			actions: {
				methodToDispatch: () => { return Promise.resolve(); }
			}
		};

		const wrapper = mount(<SampleConnectedComponentForm {...props}/>);
		const saveButton = wrapper.find('input').last();
		expect(saveButton.prop('type').toBe('submit'));
		saveButton.simulate('click');

		expect(wrapper.state().errors.title).toBe('Title is empty.');
	});
});
// JS, React, ES6, Testing, Enzyme, Mocha, Template

import expect from 'expect';
import React from 'react';
import {mount, shallow} from 'enzyme';
import SampleForm from './SampleForm';

function setup(boolProp){
	const props = {
		objProp: {}, boolProp: boolProp, 
		funcProp: () => {}
	};

	return shallow(<SampleForm {...props}/>);
}

describe('Testing via Enzyme', ()=> {
	it('renders form and h1', ()=>{
		const wrapper = setup(false);
		expect(wrapper.find('form').length).toBe(1);
		expect(wrapper.find('h1').text()).toEqual('The Title');
	});

	it('save button is labeled "Save" when boolProp is false', ()=>{
		const wrapper = setup(false);
		expect(wrapper.find('input').props().value).toBe('Save');
	});

	it('save button is labeled "Saving..." when boolProp is true', ()=> {
		const wrapper = setup(true);
		expect(wrapper.find('input').props().value).toBe('Saving...');
	});
});
Action Creators and Reducers

They don't depend any other class, pretty straightforward.

Thunks

Mock two things: Store and HTTP Calls. Store --> redux-mock-store HTTP Calls --> nock

import expect from 'expect';
import * as fooActions from './fooActions';
import thunk from 'redux-thunk';
import nock from 'nock';
import configureMockStore from 'redux-mock-store';
import types from './actionTypes';

const middleware = [thunk];
const mockStore = configureMockStore(middleware);

describe('Async actions', () => {
    afterEach(() => {
        nock.cleanAll();
    });
    
    it('should create SAMPLE_ACTION when loading result from another server', (done) => {
        nock('http://example.com/')
            .get('/sample')
            .reply(200, {body: { foo: {[{bar: "a"}], "bar" : 3 }}});
            
        const expectedAction = {type: types.SAMPLE_ACTION, body: { foo: {[{bar: "a"}], "bar" : 3 }}};
        const store = mockStore({foo: {}}, expectedAction);
        
        store.dispatch(fooActions.fooThunk()).then(() => {
            const actions = store.getActions();
            expect(actions[0].type).toEqual(types.SAMPLE_ACTION);
            done();  // tells mocha that async work is complete.
        });
    });
});

Testing inside mapStateToProps:
  • If there is a logic in mapStateToProps, extract it to a method and combine all the methods in a different file.
  • Then write a test spec for the method.

Disclaimer: Not a solution to check if that method is called in mapStateToProps

Reminder
  • shallow -> Renders one layer deep.
  • mount -> To render component with a child.