javascript - 在 React Saga 单元测试中需要帮助
问题描述
我在测试我的传奇功能时遇到了一个问题:
function * onSaveDATA() {
try {
yield put( showStatusMessage({ messageContent: 'Saving Your Data' }));
const body = yield select( state => state.appData.userDetails );
yield call( postDATA, { body });
yield put( hideStatusMessage());
yield put({ type: ActionTypes.SAVE_DATA_OK });
} catch ( e ) {
yield put({ type: ActionTypes.CRITICAL_ERROR_OCCURED, payload: e });
}
}
export function * save_on_change( ) {
yield takeEvery( ActionTypes.SAVE_DATA_REQ, onSaveDATA );
}
这是我为测试此功能而编写的单元测试,但测试失败。我不确定出了什么问题。
import { runSaga } from 'redux-saga';
import { postDATA } from './../../../../services/my_service';
// import { openModalMessage } from './../../../../actions';
import { saveDATA } from './';
jest.mock( './../../../../services/my_service' );
jest.mock( './../../../../actions' );
describe( 'Saga: Save Data', () => {
test( 'saveDATA OK', async () => {
postDATA.mockReset();
postDATA.mockReturnValue( {s:'Somevalue'} );
const dispatchedActions = [];
await runSaga({
dispatch: action => dispatchedActions.push( action ),
getState: () => ({
appState: {},
appData: { userDetails: {name:'mock-name'}},
}),
}, save_on_change );
expect( postDATA ).toHaveBeenCalled();
});
});
当我运行它时,它失败了。我不确定我在这里错过了什么。是不是因为该saveDATA
函数使用工厂函数takeEvery。我是否需要明确触发 SAVE_DATA_REQ 操作?
解决方案
这是单元测试解决方案"redux-saga": "^1.1.3"
:
index.ts
:
import { put, select, call, takeEvery } from 'redux-saga/effects';
import { postDATA } from './service';
export const ActionTypes = {
SAVE_DATA_OK: 'SAVE_DATA_OK',
CRITICAL_ERROR_OCCURED: 'CRITICAL_ERROR_OCCURED',
SAVE_DATA_REQ: 'SAVE_DATA_REQ',
};
const showStatusMessage = (payload) => ({ type: 'SHOW_STATUS_MESSAGE', payload });
const hideStatusMessage = () => ({ type: 'HIDE_STATUS_MESSAGE' });
export function* onSaveDATA() {
try {
yield put(showStatusMessage({ messageContent: 'Saving Your Data' }));
const body = yield select((state) => state.appData.userDetails);
yield call(postDATA, { body });
yield put(hideStatusMessage());
yield put({ type: ActionTypes.SAVE_DATA_OK });
} catch (e) {
yield put({ type: ActionTypes.CRITICAL_ERROR_OCCURED, payload: e });
}
}
export function* save_on_change() {
yield takeEvery(ActionTypes.SAVE_DATA_REQ, onSaveDATA);
}
service.ts
:
export async function postDATA(data) {
return { s: 'real data' };
}
index.test.ts
:
import { runSaga } from 'redux-saga';
import { onSaveDATA, ActionTypes, save_on_change } from './';
import { postDATA } from './service';
import { mocked } from 'ts-jest/utils';
import { takeEvery } from 'redux-saga/effects';
jest.mock('./service');
describe('62952662', () => {
afterAll(() => {
jest.resetAllMocks();
});
describe('onSaveDATA', () => {
test('should save data', async () => {
mocked(postDATA).mockResolvedValueOnce({ s: 'Somevalue' });
const dispatchedActions: any[] = [];
await runSaga(
{
dispatch: (action) => dispatchedActions.push(action),
getState: () => ({
appState: {},
appData: { userDetails: { name: 'mock-name' } },
}),
},
onSaveDATA,
).toPromise();
expect(postDATA).toBeCalledWith({ body: { name: 'mock-name' } });
expect(dispatchedActions).toEqual([
{ type: 'SHOW_STATUS_MESSAGE', payload: { messageContent: 'Saving Your Data' } },
{ type: 'HIDE_STATUS_MESSAGE' },
{ type: ActionTypes.SAVE_DATA_OK },
]);
});
test('should handle error if postDATA error', async () => {
const mError = new Error('network');
mocked(postDATA).mockRejectedValueOnce(mError);
const dispatchedActions: any[] = [];
await runSaga(
{
dispatch: (action) => dispatchedActions.push(action),
getState: () => ({
appState: {},
appData: { userDetails: { name: 'mock-name' } },
}),
},
onSaveDATA,
).toPromise();
expect(postDATA).toBeCalledWith({ body: { name: 'mock-name' } });
expect(dispatchedActions).toEqual([
{ type: 'SHOW_STATUS_MESSAGE', payload: { messageContent: 'Saving Your Data' } },
{ type: ActionTypes.CRITICAL_ERROR_OCCURED, payload: mError },
]);
});
});
describe('save_on_change', () => {
test('should wait for every SAVE_DATA_REQ action and call onSaveDATA', () => {
const gen = save_on_change();
expect(gen.next().value).toEqual(takeEvery(ActionTypes.SAVE_DATA_REQ, onSaveDATA));
expect(gen.next().done).toBeTruthy();
});
});
});
带有覆盖率报告的单元测试结果:
PASS src/stackoverflow/62952662/index.test.ts
62952662
onSaveDATA
✓ should save data (6 ms)
✓ should handle error if postDATA error (2 ms)
save_on_change
✓ should wait for every SAVE_DATA_REQ action and call onSaveDATA (1 ms)
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 95 | 100 | 83.33 | 93.75 |
index.ts | 100 | 100 | 100 | 100 |
service.ts | 50 | 100 | 0 | 50 | 2
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.928 s, estimated 3 s
推荐阅读
- python - Python - Mailosaur 获取 HTML 元素
- laravel - 如何在编译后的视图文件中执行 javascript(使用 View::render 方法)
- javascript - 如何从超级代理响应中提取 cookie?
- javascript - npm:在上下文中执行?
- javascript - Javascript - 将字符串中的所有字母转换为大写或小写
- python - Python Docker Container:如何安装模块,例如 pymqsl?
- html - HTML 图像标记导致错误“解析 'srcset' 属性值失败,因为它有一个未知的描述符”
- c# - 创建一个通用设置器,我可以使用它来设置类中的任何属性
- node.js - 我已经下载了 nodejs 以进行反应,但是当我的 powershell 打开以安装外部工具时,它给出了图像中显示的错误:-
- excel - Selenium + Chromedriver + Excel VBA。在单个代码中多次更改下载目录