Skip to content

Instantly share code, notes, and snippets.

@skynyrd
Last active April 20, 2020 04:42
Show Gist options
  • Save skynyrd/563aba5d41dd89ad04040cc981e99f8a to your computer and use it in GitHub Desktop.
Save skynyrd/563aba5d41dd89ad04040cc981e99f8a to your computer and use it in GitHub Desktop.
Testing React & Redux app w templates using Enzyme, Mocha and Expect
Reminder
  • shallow -> Renders one layer deep.
  • mount -> To render component with a child.
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

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.
        });
    });
});
// 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...');
});
});
// 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.');
});
});
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);
  });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment