Search code examples
reactjsintegration-testingredux-sagaredux-saga-test-plan

redux saga is returning SagaTestError: put expectation unmet when checking api call


I'm testing a simple redux saga function using redux test plan, the get posts test is returning a failure

SagaTestError: 
put expectation unmet:

Expected
--------
{ '@@redux-saga/IO': true,
  combinator: false,
  type: 'PUT',
  payload: { channel: undefined, action: { type: 'GET_POSTS_INIT' } } }

I'm testing to check if the posts array is empty being that the server is not running, is this the right approach to testing this function ?

post saga test

it(" fetchs post failure ", () => { // passes
    const error = new Error("Whoops");
    return expectSaga(getPosts)
        .provide([[call(api.post.getPosts), throwError(error)]])
        .put({ type: types.GET_POSTS_FAILURE, error: error })
        .run();
});

it("should test fetches posts", () => { // this test doesn't pass
    const posts = { posts: [] }; // is this the right approach ?
    return expectSaga(watchPosts)
        .provide([[call(api.post.getPosts), posts]])
        .put({ type: types.GET_POSTS_INIT })
        .dispatch({ type: types.GET_POSTS_SUCCESS, payload: posts })
        .silentRun();
});

post saga

export function* getPosts() {
    try {
        const posts = yield call(api.post.getPosts); // call api from axios express back end
        yield put(actionTypes.getPostsSuccess(posts));
    } catch (error) {
        yield put(actionTypes.getPostsFailure(error));
    }
}

export function* watchPosts() {
    yield takeLatest(types.GET_POSTS_INIT, getPosts);
}

Solution

  • You need to dispatch types.GET_POSTS_INIT action to watchPosts saga and put types.GET_POSTS_SUCCESS action with payload posts.

    E.g.

    index.ts:

    import { call, takeLatest, put } from 'redux-saga/effects';
    import { api } from './api';
    import { types } from './types';
    
    const actionTypes = {
      getPostsSuccess(posts) {
        return { type: types.GET_POSTS_SUCCESS, payload: { posts } };
      },
      getPostsFailure(error) {
        return { type: types.GET_POSTS_FAILURE, error };
      },
    };
    
    export function* getPosts() {
      try {
        const posts = yield call(api.post.getPosts);
        yield put(actionTypes.getPostsSuccess(posts));
      } catch (error) {
        yield put(actionTypes.getPostsFailure(error));
      }
    }
    
    export function* watchPosts() {
      yield takeLatest(types.GET_POSTS_INIT, getPosts);
    }
    

    api.ts:

    export const api = {
      post: {
        async getPosts() {
          return 'mocked posts';
        },
      },
    };
    

    types.ts:

    export const types = {
      GET_POSTS_INIT: 'GET_POSTS_INIT',
      GET_POSTS_FAILURE: 'GET_POSTS_FAILURE',
      GET_POSTS_SUCCESS: 'GET_POSTS_SUCCESS',
    };
    

    index.test.ts:

    import { getPosts, watchPosts } from './';
    import { api } from './api';
    import { types } from './types';
    import { expectSaga } from 'redux-saga-test-plan';
    import { throwError } from 'redux-saga-test-plan/providers';
    import { call } from 'redux-saga/effects';
    
    describe('62105055', () => {
      it(' fetchs post failure ', () => {
        const error = new Error('Whoops');
        return expectSaga(getPosts)
          .provide([[call(api.post.getPosts), throwError(error)]])
          .put({ type: types.GET_POSTS_FAILURE, error: error })
          .run();
      });
    
      it('should test fetches posts', () => {
        const posts = { posts: [] };
        return expectSaga(watchPosts)
          .provide([[call(api.post.getPosts), posts]])
          .put({ type: types.GET_POSTS_SUCCESS, payload: { posts } })
          .dispatch({ type: types.GET_POSTS_INIT })
          .silentRun();
      });
    });
    

    unit test result with coverage report:

     PASS  src/stackoverflow/62105055/index.test.ts
      62105055
        ✓  fetchs post failure  (11 ms)
        ✓ should test fetches posts (252 ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |   93.75 |      100 |      80 |   93.33 |                   
     api.ts   |      50 |      100 |       0 |      50 | 4                 
     index.ts |     100 |      100 |     100 |     100 |                   
     types.ts |     100 |      100 |     100 |     100 |                   
    ----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        4.166 s
    

    package versions:

    "redux-saga": "^1.1.3",
    "redux-saga-test-plan": "^4.0.0-rc.3",
    "typescript": "^3.9.7",
    "jest": "^26.1.0",