[英]How to unit test promises in react/redux application
查看react-redux站点上的高级示例,使用mocha
、 chai
和sinon
对fetchPosts
函数进行单元测试的最佳方法是什么? 它使用承诺链,因此鉴于此,我查看了chai-as-promised
。
承诺链与最后一个解析为空值并分派操作以更新 redux 状态。
应用代码:
export function fetchPosts(subreddit) {
return dispatch => {
dispatch(requestPosts(subreddit))
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(subreddit, json)))
}
}
//other functions
export function requestPosts(subreddit){
return {
type:REQUEST_POSTS,
subreddit
}
}
export function receivePosts(subreddit,json){
return {
type:RECEIVE_POSTS,
subreddit,
posts:json.data.children.map(child => child.data),
receivedAt:Date.now()
}
}
我的单元测试尝试:
describe('fetchPosts action creator', () => {
it('should have one parameter', () => {
expect(fetchPosts.length).to.equal(1);
});
it('should return a function holding one parameter', () => {
let subreddit = 'frontend';
expect(fetchPosts(subreddit).length).to.equal(1);
});
it('should execute the dispatch funcion with requestPosts' ,() =>{
let spy = spy(dispatch);
let subreddit = 'frontend';
expect(spy.calledWith(fetchPosts(subreddit))).to.be.calledOnce;
expect(spy.calledWith(receivePosts(subreddit,json))).to.be.calledOnce;
})
})
我的想法是
fetchPosts()
可以打到 reddit apidispatch
与被调用两次requestPosts
和receivePosts
您可以遵循redux-mock-store包的异步操作。
用于测试 Redux 异步操作创建者和中间件的模拟商店。 模拟存储将创建一个调度操作数组,用作测试的操作日志。
此外,您应该存根fetch
和Date.now()
函数。
这样它就不会通过真实网络发送 HTTP 请求,也不依赖于真实系统时间(本地机器、CI/CD 服务器),允许您的单元测试在隔离的、无副作用的环境中运行。
例如
actionCreators.ts
:
const REQUEST_POSTS = 'REQUEST_POSTS';
const RECEIVE_POSTS = 'RECEIVE_POSTS';
export function fetchPosts(subreddit) {
return (dispatch) => {
dispatch(requestPosts(subreddit));
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then((response) => response.json())
.then((json) => dispatch(receivePosts(subreddit, json)));
};
}
export function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
subreddit,
};
}
export function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
subreddit,
posts: json.data.children.map((child) => child.data),
receivedAt: Date.now(),
};
}
actionCreators.test.ts
:
import createMockStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import { fetchPosts } from './actionCreators';
import sinon from 'sinon';
import { expect } from 'chai';
const middlewares = [thunk];
const mockStore = createMockStore<{}, ThunkDispatch<{}, any, AnyAction>>(middlewares);
describe('46791110', () => {
afterEach(() => {
sinon.restore();
});
it('should pass', () => {
const store = mockStore({});
const mRes = { json: sinon.stub().resolves({ data: { children: [{ data: 'jav' }, { data: 'big xx' }] } }) };
const fetchStub = sinon.stub().resolves(mRes);
const dateStub = sinon.stub(Date, 'now').returns(111);
global.fetch = fetchStub;
return store.dispatch(fetchPosts('nsfw')).then(() => {
const actions = store.getActions();
expect(actions).to.be.deep.equal([
{ type: 'REQUEST_POSTS', subreddit: 'nsfw' },
{
type: 'RECEIVE_POSTS',
subreddit: 'nsfw',
posts: ['jav', 'big xx'],
receivedAt: 111,
},
]);
sinon.assert.calledOnceWithExactly(fetchStub, 'https://www.reddit.com/r/nsfw.json');
sinon.assert.calledOnce(mRes.json);
sinon.assert.calledOnce(dateStub);
});
});
});
单元测试结果:
PASS examples/46791110/actionCreators.test.ts (11.183 s)
46791110
✓ should pass (5 ms)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
actionCreators.ts | 100 | 100 | 100 | 100 |
-------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.214 s
源代码: https : //github.com/mrdulin/jest-v26-codelab/tree/main/examples/46791110
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.