Running runSaga on my redux saga generator function my window variable is thrown as undefined but my test files are passing is there any way to mock the window variable?
Below is my redux saga generator function
import api from 'my-api';
const getSuburb = () => window.userCookies.selectedSuburb;
function* saga(payload) {
const action = yield take('REQUEST_LIBRARY');
const selectedSuburb = yield call(getSuburb);
const getStateLibraries = yield call(api.getLibraries, selectedSuburb, action.userId);
yield put(loadLibrary(getStateLibraries)
}
On running the above code I receive list of libraries with respect to the suburb, I have another state holds the suburb information where I can use select to retrieve it. The code works fine
Unit test cases to test redux saga using RunSaga
const recordSaga = async function (sagaHandler, initalAction) {
const dispatchedActions = [];
const fakeStore = {
getState: () => (initialState),
dispatch: action => dispatchedActions.push(action),
};
await runSaga(
fakeStore,
sagaHandler,
initalAction,
).done;
return dispatchedActions;
};
describe('Run Saga', () => {
it('should dispatch action libraries', async() => {
const dispatched = await recordSaga(saga, { user_id:2 });
expect(dispatched).toContainEqual(loadLibrarySuccess(someProfile));
}
While running the above I am getting on selectedSuburb
as undefined because window.userCookies.totalSuburbs is not defined, is there any better way to mock the getSuburb
function?
I recommend to look at how Redux-Saga docs suggest sagas testing. In unit test only logic of function saga
should be tested and not calls to any third party function. And yield
keyword greatly helps with this.
Essentially we test that generator function (function saga
in this example) will return correct objects on each call. So unit test for saga
function will look like (not tested, just sample)
describe('Run Saga', () => {
it('should dispatch action libraries', () => {
const gen = saga() // As saga is generator function it will return generator object
expect(gen.next().value).toStrictEqual(take('REQUEST_LIBRARY'))
expect(gen.next({ userId: 'user1' } /* here can be action returned by take('REQUEST_LIBRARY') in saga */).value).toStrictEqual(call(getSuburb))
expect(gen.next({ suburb: 'central suburb' } /* here can be selectedSuburb returned by call(getSuburb) in saga */).value).toStrictEqual(call(api.getLibraries, { suburb: 'central suburb' }, 'user1'))
// The values of selectedSuburb and action.userId are from calls to gen.next()
expect(gen.next({ libraries: ['lib 1', 'lib 2'] } }).value).toStrictEqual(put(loadLibrary({ libraries: ['lib 1', 'lib 2'] }))
expect(gen.next().done).toBe(true); // Saga is finished
}
})
So unit test only checks saga itself and don't call any functions that saga calls when executed. So you don't need to mock anything at all when using sagas. This confirm to idea of unit testing to test only single unit of code (function) in isolation.
As you may noticed, value returned from yield
should be passed to next()
function but on next line. This is how generator works.
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