繁体   English   中英

如何在 react/redux 应用程序中对 promise 进行单元测试

[英]How to unit test promises in react/redux application

查看react-redux站点上的高级示例,使用mochachaisinonfetchPosts函数进行单元测试的最佳方法是什么? 它使用承诺链,因此鉴于此,我查看了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 api
  • 检查响应是否有效 json
  • 检查dispatch与被调用两次requestPostsreceivePosts

您可以遵循redux-mock-store包的异步操作

用于测试 Redux 异步操作创建者和中间件的模拟商店。 模拟存储将创建一个调度操作数组,用作测试的操作日志。

此外,您应该存根fetchDate.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM