Redux: How to test a connected component?
Solution 1
This is an interesting question.
I usually do import both container and component to do the testing. For container testing I use, redux-mock-store
. Component testing is for testing async functions. For instance in your case, login process is an async function using sinon
stubs. Here is a snippet of the same,
import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { stub } from 'sinon';
const mockStore = configureMockStore([thunk]);
describe('Container Login', () => {
let store;
beforeEach(() => {
store = mockStore({
auth: {
sport: 'BASKETBALL',
},
});
});
it('should render the container component', () => {
const wrapper = mount(
<Provider store={store}>
<LoginContainer />
</Provider>
);
expect(wrapper.find(LoginContainer).length).to.equal(1);
const container = wrapper.find(LoginContainer);
expect(container.find(Login).length).to.equal(1);
expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
});
it('should perform login', () => {
const loginStub = stub().withArgs({
username: 'abcd',
password: '1234',
});
const wrapper = mount(<Login
loginUser={loginStub}
/>);
wrapper.find('button').simulate('click');
expect(loginStub.callCount).to.equal(1);
});
});
Solution 2
As you pointed out, the way I usually do this is to export the un-connected component as well, and test that.
i.e.
export {Login};
Here's an example. Source of the component, and source of the tests.
For the wrapped component, I don't author tests for those because my mappings (mapStateToProps
and mapDispatchToProps
) are generally very simple. If I wanted to test a wrapped component, I'd really just be testing those maps. So those are what I would choose to explicitly test, rather than re-testing the entire component in a wrapped form.
There are two ways to test those functions. One way would be to export the functions within the module itself.
i.e.;
export {mapStateToProps, mapDispatchToProps}
I'm not a huge fan of this, because I wouldn't want other modules in the app to access them. In my tests, I sometimes use babel-plugin-rewire to access "in-scope" variables, so that's what I would do in this situation.
That might look something like:
import {
Login, __Rewire__
}
const mapStateToProps = __Rewire__.__get__('mapStateToProps');
describe('mapStateToProps', () => { ... });
Related videos on Youtube
Umair Sarfraz
Updated on June 06, 2022Comments
-
Umair Sarfraz almost 2 years
I am using
Enzyme
to unit test my React components. I understand that in order to test the raw unconnected component I'd have to just export it and test it (I've done that). I have managed to write a test for the connected component but I am really not sure if this's the right way and also what exactly would I want to test for the connected component.Container.jsx
import {connect} from 'react-redux'; import Login from './Login.jsx'; import * as loginActions from './login.actions'; const mapStateToProps = state => ({ auth: state.auth }); const mapDispatchToProps = dispatch => ({ loginUser: credentials => dispatch(loginActions.loginUser(credentials)) }); export default connect(mapStateToProps, mapDispatchToProps)(Login);
Container.test.js
import React from 'react'; import {Provider} from 'react-redux'; import {mount, shallow} from 'enzyme'; import {expect} from 'chai'; import LoginContainer from '../../src/login/login.container'; import Login from '../../src/login/Login'; describe('Container Login', () => { it('should render the container component', () => { const storeFake = state => ({ default: () => { }, subscribe: () => { }, dispatch: () => { }, getState: () => ({ ...state }) }); const store = storeFake({ auth: { sport: 'BASKETBALL' } }); const wrapper = mount( <Provider store={store}> <LoginContainer /> </Provider> ); expect(wrapper.find(LoginContainer).length).to.equal(1); const container = wrapper.find(LoginContainer); expect(container.find(Login).length).to.equal(1); expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' }); }); });
-
Umair Sarfraz over 7 yearsI've done that.
import Login from ../login
is the un-connected component. The reason I haven't used {Login} is because they lie in separate files. -
Umair Sarfraz over 7 yearsAlso, what exactly do I need to test for the container component?
-
jamesplease over 7 yearsOh, I see what you mean. So far I haven't tested those tbqh. If I did test them, I would use something like
babel-plugin-rewire
and testmapStateToProps
andmapDispatchToProps
rather than the wrapped component itself. -
Umair Sarfraz over 7 years
mapStateToProps
andmapDispatchToProps
how? Can you provide an example please? -
Umair Sarfraz over 7 yearsAlso, have you done the integration testing? I've been able to perform that but I have some async actions that return functions. Any idea how I can test them? I can create a separate question for it.
-
jamesplease over 7 yearsUpdated the answer @umair. Let me know if that helps.
-
Umair Sarfraz over 7 yearsI wouldn't want to use
babel-plugin-rewire
tbh. And I am not really sure what you meant in the first option. -
jamesplease over 7 yearsYour mapping functions are just variables, so you can export them from the module. Then, you can import them in your test to access them. In the same way that you're doing
export {Login}
and thenimport {Login}
in your test, you could doexport {Login, mapStateToProps}
andimport {Login, mapStateToProps}
. -
Michael Parker over 7 yearsI personally have been following the pattern of exporting
mapStateToProps
,mapDispatchToProps
, andmergeProps
for the sake of testing them. I had no idea thatbabel-plugin-rewire
was an option - I'll have to try this out some time. -
Umair Sarfraz over 7 yearsWould that suffice the tests for mapStateToProps and mapDispatch?
-
anoop over 7 yearsYes indeed.. You must check with a coverage tool, u ll find it is fully covered
-
Umair Sarfraz over 7 yearsThe actual thing I want to know is basically WHAT to test when we are going to test the container/smart component?
-
Umair Sarfraz over 7 yearsHave you done integration testing? I've been doing the unit testing but I've read around that we should write integration tests as well. If yes, I've got an important question that I can share the link
-
Umair Sarfraz over 7 yearsAnd by integration testing I mean triggering the action and asserting on the store update
-
anoop over 7 yearsNot exactly I basically mount the component and simulate events and assert it.. But may be this can give u a better idea skillsmatter.com/skillscasts/…
-
Umair Sarfraz over 7 yearsThanks a million. Have you worked with 'nock' for API calls?
-
anoop over 7 yearsyes, but I prefer moxios and sinon.. one reason is i use axios for network calls
-
Umair Sarfraz over 7 yearsI'm using axios as well. I've actually never heard about moxios. Is that any better?
-
Umair Sarfraz over 7 yearsLet us continue this discussion in chat.
-
Umair Sarfraz over 7 years
-
Umair Sarfraz over 7 years
-
PositiveGuy about 7 yearswhen I try to mount with provider, I get "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined."
-
PositiveGuy about 7 yearscan you show us your actual Container Component implementation?
-
anoop about 7 years@PositiveGuy I think the component is not exported correctly