Search code examples
unit-testingreduxjestjsredux-sagaredux-saga-test-plan

Redux Saga Unit Testing with jest: How to sustain the object received by take in the test?


I'm new to Unit testing, Redux and Redux Saga. I use it with React Native. I just implemented my loginFlow just like the one described in the docs of Redux Saga:

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    // fork return a Task object
    const task = yield fork(authorize, user, password)
    const action = yield take(['LOGOUT', 'LOGIN_ERROR'])
    if (action.type === 'LOGOUT')
      yield cancel(task)
    yield call(Api.clearItem, 'token')
  }
}

function* authorize(user, password) {
  try {
    const token = yield call(Api.authorize, user, password)
    yield put({type: 'LOGIN_SUCCESS', token})
    yield call(Api.storeItem, {token})
    return token
  } catch(error) {
    yield put({type: 'LOGIN_ERROR', error})
  } finally {
    if (yield cancelled()) {
      // ... put special cancellation handling code here
    }
  }
}

I'm now trying to write tests for this Auth flow. Unfortunately iI can't figure out how to give the test for the *loginFlow generator the email and the password. Here is where I'm currently at:

describe("login flow", () => {
  const gen = loginFlow();
  const user = "test@test.test";
  const password = "testpassword";

  it("should wait for a user to log in", () => {
    expect(gen.next({ user, password }).value).toEqual(take(LOGIN_REQUEST));
  });

  it("should fork the handling of the login request", () => {
    const expectedYield = fork(authorize, user, password);
    expect(gen.next().value).toEqual(expectedYield);
  });
});

The problem is, that this throws the error:

● login flow › should fork the handling of the login request

    TypeError: Cannot read property 'email' of undefined

      28 | export function* loginFlow() {
      29 |   while (true) {
    > 30 |     const { email, password } = yield take(LOGIN_REQUEST);

         |             ^
      31 |     // fork return a task object
      32 |     const task = yield fork(authorize, email, password);
      33 |     const action = yield take([LOGOUT, LOGIN_ERROR]);

As you can see I tried to give these values to the yield keyword by supplying them in the next call, but it just won't work.


Solution

  • After trying a lot I just found it out.

    Here is how I had to change the test:

    describe("login flow", () => {
      const gen = loginFlow();
      const email = "test@test.test";
      const password = "testpassword";
    
      it("should wait for a user to log in", () => {
        expect(gen.next().value).toEqual(take(LOGIN_REQUEST));
      });
    
      it("should fork the handling of the login request", () => {
        const expectedYield = fork(handleLoginRequest, email, password);
        expect(gen.next({ email, password }).value).toEqual(expectedYield);
      });
    });