redux-saga / redux-saga

An alternative side effect model for Redux apps

Home Page:https://redux-saga.js.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unit test for api polling done on Redux-saga side

soumyaa1804 opened this issue · comments

Description

I am polling an API and using similar code as given in this code pen
I am trying to test the saga part of it using Jest but I am confused how to test a generator function with a delay and while loop.
In the codepen the delay is a function:

const delay = (ms) => new Promise((res) => setTimeout(res, ms));

Can someone please help here?

Greetings!

In the codepen the actual code where delay is being used looks like:

yield call(delay, 400);

When you run a generator for testing, you can step through that yield by calling generator.next().

It's hard to explain without seeing the test you've written so far, could you share that here?

Sure. The following is the saga that I have. Here getData is the data service we have created which is simple api fetching function

const delay = (ms) => new Promise((res) => setTimeout(res, ms));
const url = 'https://08ad1pao69.execute-api.us-east-1.amazonaws.com/dev/random_joke';

function* pollSaga(action) {
  while (true) {
    try {
      const { data } = yield call(getData, url);
      yield put(getDataSuccessAction(data));
      yield call(delay, 4000);
    } catch (err) {
      yield put(getDataFailureAction(err));
    }
  }
}

And this is the test I have written so far:

describe('API polling', () => {
	jest.useFakeTimers();
	const url = 'https://08ad1pao69.execute-api.us-east-1.amazonaws.com/dev/random_joke';

	it('should call success action', () => {
		const gen = pollSaga(action);
		fetch.resetMocks();
		fetch.mockResponseOnce(JSON.stringify(pollingSuccessResponse));
		expect(gen.next(mockSuccessPolling).value).toEqual(
			call(getData, url)
		);
		expect(gen.next(mockSuccessPolling).value).toEqual(
			put(getDataSuccessAction(pollingSuccessResponse))
		);
		expect(gen.next().done).toBeTruthy();
	});

	it('should be failure', () => {
		const gen = pollSaga(action);
		fetch.resetMocks();
		fetch.mockResponseOnce(JSON.stringify(failureResponse));
		expect(gen.next(mockSuccessPolling).value).toEqual(
			call(getData, url)
		);
		expect(gen.throw(failureResponse.statusCode).value).toEqual(
			put(getDataFailureAction(505))
		);

		expect(gen.next().done).toBeTruthy();
	});
});

These tests are failing. I suppose as I have not considered the while loop and also the delay that is why it is failing. Please let me know what I am doing wrong?! Thanks

Anything that is wrapped in yield call doesn't actually run any side-effects, it merely describes the side-effect that ought to be called. In this way, there is no need to mock your fetch. The code I wrote below confirms that you are dispatching the getDataSuccessAction

For the success:

const gen = pollSaga();
console.log(gen.next().value); // call(getData)

const dataSuccess = gen.next({ data: '123' }).value;
assert.deepEqual(dataSuccess, put(getDataSuccessAction('123')));

console.log(gen.next({ data: '123' }).value); // put(getDataSuccessAction)
console.log(gen.next().value); // call(delay)

For the failure test, you just need to throw the generator at the right place to have to properly go into the catch statement:

const gen = pollSaga();
gen.next(); // call(getData)
const dataError = gen.throw(new Error("something"));
assert.deepEqual(dataError.value.payload.action.payload.message, "something");

I'm going to close this issue for now. If you -- or anyone else -- still has issues with testing api polling please let me know here!