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);
}
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",