I am writing test cases for my application bases on react and redux.
container = TestUtils.renderIntoDocument(
<Provider store={createStore({"key": "old_val"})}>
{() => <Component />}
</Provider>
);
After rendering with initialState
, I dispatch an action and see if the state changes.
Component.store.dispatch({ type: 'SET_VAL', value: 'some_val' });
Then I print the state
console.log(store.getState());
I expect the state to be {"key": "some_val"}
. However, it still shows {"key": "old_val"}
.
The application works fine, just not the test, so there can't be any problem with my action-creators
or reducers
.
Am I doing something wrong here? Btw, I am using thunk
middleware for async action dispatch. Does that interfere here? If yes, how do I wait till the async action gets completed?
Update:
The redux tests shown here are pretty straightforward, yet they seem to work fine.
store.dispatch(addTodo('Hello'));
expect(store.getState()).toEqual([{
id: 1,
text: 'Hello'
}]);
The app code dispatches an action to the Redux store, like dispatch({type: 'counter/incremented'}) The store runs the reducer function again with the previous state and the current action , and saves the return value as the new state.
The store is a “container” (really a JavaScript object) that holds the application state, and the only way the state can change is through actions dispatched to the store. Redux allows individual components connect to the store and apply changes to it by dispatching actions.
Dispatching an action within a reducer is an anti-pattern. Your reducer should be without side effects, simply digesting the action payload and returning a new state object. Adding listeners and dispatching actions within the reducer can lead to chained actions and other side effects.
One of the huge benefits of redux is that it allows you to implement almost all of your application using pure functions and pure components. Redux and react-redux abstract the implementation details of subscribing UI to state changes which allows you to test all of your app's code in isolation. This way you don't need to render out a provider with a store every time you want to test out your code, which is a major reduction in complexity.
Let's say you have a key
property in your state and a KeyDisplay
component. You can implement the state with the following reducer file:
reducers.js
import { combineReducers } from 'redux';
export function key(state, { type, value }) {
switch(type) {
case 'SET_VAL': return value;
default: return state;
}
}
export default combineReducers({ key });
And you can set up a file for our component:
KeyDisplay.js
import React from 'react';
import { connect } from 'react-redux';
export function KeyDisplay({ keyProp }) {
return (
<div>The key is {keyProp}</div>
);
}
export default connect((state) => { keyProp: state.key })(KeyDisplay);
Then in the unit test for the reduce you can import just the reducer for key
and test it totally separate of the user interface:
keyReducer.test.js
import test from 'tape';
import { key } from './reducers.js';
test('key reducer', (t) => {
t.plan(1);
const output = key('old', { type: 'SET_VAL', value: 'new' });
t.equal(output, 'new', 'SET_VAL should override old value');
});
Additionally, since connect
passes state as props into the component you can just render the unconnect
ed component with some test props that represent a state you are interested in, again without setting up a store and provider:
KeyDisplay.test.js
import test from 'tape';
import { renderIntoDocument } from 'react-addons-test-utils';
import { KeyDisplay } from './KeyDisplay.js';
test('KeyDisplay', (t) => {
const component = renderIntoDocument(<KeyDisplay keyProp="test" />);
// test component
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With