-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
testing async store.dispatch #546
Comments
You can have a callback by using
You probably want to clean up the subscriber after the test
|
I didn't even come to mind within the context of testing, that's a great solution. I'm assuming it fires after a dispatch and not at some arbitrary time. Thanks! |
I'm reopening so somebody can contribute this to the Writing Tests recipe. Please write here if you make a PR! |
Glad I found this issue! Was racking my brain trying to figure out something elegant here. Thanks! |
Rather than this: var unsubscribe = store.subscribe(function() {
expect(store.getState()).toEqual(...);
done();
});
// Some async dispatch
actions.authenticate()( store.dispatch ) ; you can just write this: var unsubscribe = store.subscribe(function() {
expect(store.getState()).toEqual(...);
done();
});
// Some async dispatch
store.dispatch(actions.authenticate()); as long as your |
I'm confusing about testing async action dispatch I'd like to test like below function postOauthTokenRequest() {
return {
type: POST_OAUTH_TOKEN_REQUEST
};
}
function postOauthTokenSuccess(body) {
return {
type: POST_OAUTH_TOKEN_SUCCESS,
body
};
}
function postOauthTokenFailure(ex) {
return {
type: POST_OAUTH_TOKEN_FAILURE,
ex
};
}
export function postOauthToken(data) {
return dispatch => {
dispatch(postOauthTokenRequest());
return fetcher.post('http://localhost:3000/oauth/token', data)
.then(json => dispatch(postOauthTokenSuccess(json.body)))
.catch(ex => dispatch(postOauthTokenFailure(ex)));
};
} I'd like to test async request by using the sample test code above written by @gaearon , how can i test it? because I have to wait dispatching |
@gaearon I am still not able to understand how do I test async actions and mock ajax response within them and dispatch the newAction. Once I understand, I'll make sure to raise a PR in the |
You need some kind of way to mock |
One way would be to mock responses with something like |
Thanks. I thought it to be complicated. But if its as simple as mocking ajax request and nothing else then I did something like this. I am using superagent.
|
yep i've mocked my request with nock('http://localhost:3000/')
.get('/oauth/token')
.reply(200);
describe('oauth actions', () => {
it('creates POST_OAUTH_TOKEN_SUCCESS', (done) => {
store.subscribe(() => {
expect(store.getState()).toEqual(...);
done();
});
store.dispatch(actions. postOauthToken())
});
}); And I'd like to test when how can i get action type inside store subscription? sorry if i misunderstanding how to test async actions. |
Folks, this is really not any different from how you'd unit test any custom methods. Not specific to Redux at all. If you need to know action type, you need to spy on |
To give you an idea (I haven't tested though): describe('oauth actions', () => {
afterEach(() => {
nock.cleanAll();
});
it('creates POST_OAUTH_TOKEN_REQUEST and POST_OAUTH_TOKEN_SUCCESS', (done) => {
nock('http://localhost:3000/')
.get('/oauth/token')
.reply(200);
const expectedActions = [
{ type: 'POST_OAUTH_TOKEN_REQUEST' },
{ type: 'POST_OAUTH_TOKEN_SUCCESS', body: { /* ... */ } }
];
function mockDispatch(action) {
const expectedAction = expectedActions.shift();
expect(action).toEqual(expectedAction);
if (!expectedActions.length) {
done();
}
}
actions.postOauthToken(mockDispatch);
});
}); This also doesn't require you passing As you can see this has nothing to do with Redux. You're just testing a function that accepts a callback. |
Here's an example with API middleware from another thread: import apiMiddleware from '../middleware/api';
const middlewares = [apiMiddleware];
/**
* Creates a mock of Redux store with middleware.
*/
function mockStore(getState, expectedActions, onLastAction) {
if (!Array.isArray(expectedActions)) {
throw new Error('expectedActions should be an array of expected actions.');
}
if (typeof onLastAction !== 'undefined' && typeof onLastAction !== 'function') {
throw new Error('onLastAction should either be undefined or function.');
}
function mockStoreWithoutMiddleware() {
return {
getState() {
return typeof getState === 'function' ?
getState() :
getState;
},
dispatch(action) {
const expectedAction = expectedActions.shift();
expect(action).toEqual(expectedAction);
if (onLastAction && !expectedActions.length) {
onLastAction();
}
return action;
}
};
}
const mockStoreWithMiddleware = applyMiddleware(
...middlewares
)(mockStoreWithoutMiddleware);
return mockStoreWithMiddleware();
}
describe('action creators', () => {
afterEach(() => {
nock.cleanAll();
});
it('creates signup actions', (done) => {
// mock requests you need in this test
nock('http://localhost:3000/')
.get('/signup')
.reply(200);
const expectedActions = [
{ type: 'SIGNUP_REQUEST' },
{ type: 'SIGNUP_SUCCESS', payload: { /* ... */ } }
];
const mockState = {
something: 42
};
const mockStore = createMockStore(mockState, expectedActions, done);
mockStore.dispatch(signup());
});
}); |
I'd like to resurface this because it appears that nock is serverside only, unless I'm doing something incorrect? I'm using Webpack. |
@damassi If you run your tests using Node.js as the runtime, mocha as the testing framework, and jsdom to mock the browser environment then you can test "client-side" code on the "server" (I'm putting these terms in quotes, because really Node.js doesn't have to be used as a "server" - here it's acting more as a headless broswer/test harness). Thus, because you are running your tests via Node.js, you can use nock to test (jsdom will turn There are plenty of other XHR mocking libraries out there too if you'd prefer to run your unit tests inside a browser instead of Node.js (see: Sinon.js). |
thanks @idolize - Yeah, I'm doing all of these things, but through Karma as the env. Very odd! I was getting errors along the lines of can't find "fs" "http" etc -- things from the node standard library. I'll re-investigate. |
Does this technique of subscribing still work when there is an
This is my code:
If the Additionally, if not using promises, is this subscribe technique the best way to test callback oriented actions? Ex:
It seems like it might be messy keeping track of multiple dispatches in the callback. |
I got a bit fed up of writing async actions-creator tests different from sync action-creator tests, so I wrote a simple middleware that I include in the test/mock store (inserted before redux-thunk), which adds promises all the way down: const promisifyMiddleware = ({dispatch, getState}) => next => action => {
return new Promise( (resolve) => resolve(next(action)) )
} This means whether an action-creator is async or not (i.e. whether it returns a promise or not), you can test it in the same way like this: it('dispatches something', () => {
const expectedActions = [ { type: .. } ]
return store.dispatch(actions.maybeDoSomethingAsync())
.then(() => {
expect(store.getActions()).toEqual(expectedActions)
})
}) |
I just created a new blog post about it http://elh.mx/javascript/how-to-test-your-ajax-http-requests-for-actions-in-react-redux/, I think that @lucaspiller you need to include
Cause If you don't use it seems like it's never executed the expectation within the promise Am I wrong? |
@heridev if you return a promise (as timdorr does), there's no need to use |
what about using await/async?
|
I'd do the following instead:
Why? @lucaspiller answer was good until I wanted to test what happens to my action creator if promise rejects instead of resolve. Now I'm able to fully cover test of my redux action creators. Hope that helps :) |
Maybe I'm overlooking something but I'm finding it impossible to test store.dispatch with an async action without a hacky timer solution. In the below example actions.authenticate contains an ajax call. The ajax call is mocked but its still in an async situation. I'm using redux-thunk middlewear. testTimeout() is just setTimeout with a 0 second delay.
Yes I know the design of redux allows the separate testing of actions, reducers, etc, but testing store.dispatch itself is a lot of bang for the buck because it ties store, reducers, actions and middlewear together.
I could test actions and reducers together (below) bypassing store.dispatch but its incomplete because its bypassing any middlewear. Note that actions.authenticate() returns a thunk that calls mockDispatch when ready
I almost want store.dispatch() to take a callback as a second param, to be called when the dispatch is complete, but that's weird right, and only useful in testing?
The text was updated successfully, but these errors were encountered: