redux-logic
redux-logic copied to clipboard
Unit testing, how can we perform process testing using async and await keywords.
Following #21,
The example you provided works for the promise chaining processing style.
This is not the writing you recommanded to me, I have this two process that require unit test:
Global logic of logout, will be present on every pages:
export const getLogoutLogic = createLogic({
type: SUBMIT_LOGOUT_REQUEST, // trigger on this action
latest: true, // use response for the latest request when multiple
async process({ pages, authService, forwardTo }, dispatch, done) {
try {
const success = await authService.logout();
dispatch(sendingLogoutRequest(false));
forwardTo(pages.pageLogin.path);
dispatch(onSuccessLogoutRequest(success));
} catch (err) {
dispatch(sendingLogoutRequest(false));
forwardTo(pages.pageLogin.path);
dispatch(onErrorLoginRequest(err));
}
done();
},
});
Login logic, will be present on LoginPage:
export const getAuthorizeLogic = createLogic({
type: SUBMIT_LOGIN_REQUEST, // trigger on this action
cancelType: LOCATION_CHANGE, // cancel if route changes
latest: true, // use response for the latest request when multiple
async process({ authService, forwardTo, pages, action }, dispatch, done) {
const { username, password } = action.data;
try {
const jwt = await performLogin(authService, username, password);
dispatch(onSuccessLoginRequest());
dispatch(jwtLoaded(jwt));
forwardTo(pages.pageDashboard.path); // Go to dashboard page
} catch (err) {
dispatch(onErrorLoginRequest(err));
forwardTo(pages.pageLogin.path); // Go to dashboard page
}
done();
},
});
async function performLogin(authService, username, password) {
await authService.preLogin();
await authService.login(username, password);
const codeRes = await authService.code(oauthClient.clientId, oauthClient.redirectUri);
const code = getParameter('code', codeRes.url);
const jwt = await authService.token(oauthClient.clientId, oauthClient.clientSecret, code,
return jwt;
}
In your example:
const resultPromise = getAuthorizeLogic.process({
pages,
authService,
action,
forwardTo,
getState,
requestUtil
}, dispatch, done);
Even with a mock for my test, this doesn't return a promise, but instead undefined because I don't do any return in my process.
I am also surprise there is no otherway to test the code "line by line", for example, for await return value checking.
I also need a way to test when errors happen, so my app doesn't get unsynced with it's store.
Thanks in advance.
The way I think about it is that it is great to develop nice testable API's for our own code that don't depend on any specific framework thus they can be independently tested and reused in many interesting ways. Then I use code like redux-logic to tie that API to the redux API (mapping things from actions and dispatching results).
Breaking things out keeps things smaller and more focussed and usually makes testing easier. It is great to be able to test API code without having to deal with actions and dispatching. It is still good to have tests at different levels (unit, integration, system) to make sure all that works right, but we look for different things at the various levels.
So with our example above, you can test your API performLogin as a whole to make sure it does everything properly in order, and you can test each individual step (authService.preLogin, authService.login, ...) to make sure they all do the right thing and return errors appropriately.
Then when you get to testing logic code, you are primarily testing a few happy and failure paths just to make sure that everything is being mapped properly (inputs from actions, invoking the API's, and dispatching data as actions, and that errors are being dispatched as well).
In that way we are covering things at multiple levels where it makes sense but not getting bogged down in trying to reproduce every combination when working at the higher levels.
As mentioned in #21 (and for anyone coming to this conversation here) I usually do some testing of the various hooks from createLogic (the object shape returned by createLogic is basically the same as is passed in with some defaults applied), so you can test the fooLogic.process (or fooLogic.validate, fooLogic.tranform) hook by passing in the deps you use like getState, action. I have usually passed in an expect.createSpy for dispatch so I can check directly what is dispatched.
After that low level logic unit testing, I also do some integration testing at the middleware or store level so that I can verify that everything is configured and declared properly.
However I am thinking we would benefit from a helper lib that makes that a little easier to setup. It is a bit of busywork to set those things up, so I think that could be streamlined a bit. I'll put together a gist of what it could look like to see what you and others think. I'll link to it here shortly.
Take a look at this redux-logic-test gist for the test helper API and let me know what you think
I created a request for comment RFC issue for discussing the API of the new test helper #25
That look nice like this, for when do you schedule the next release incl. testing ?
I really need this, as all our code coverage get reduced because of it. Any update on the release date ?
@kopax Sorry for the delay, I just wanted to make sure I allowed some time for input from people, but it seems that this will be a good start. I should be able to work on this soon.
@jeffbski Sorry for the delay myself, I started to get to go with redux-logic, I had to teach my team how to use it and we had some other topic. I'll close as soon as it is confirmed that we know how to test every logic, which is not the case yet.
Thanks! I know how it is. We get backed up on things and have to prioritize. I deal with this daily.