【问题标题】:How to write a Redux Saga Test by mocking selectors and / or redux store如何通过模拟选择器和/或 redux 存储来编写 Redux Saga 测试
【发布时间】:2020-01-29 22:01:58
【问题描述】:

上下文:我是编写 Redux Saga 测试的新手,一直在使用 React Boilerplate 开发一个使用 Jest 进行测试的应用程序。样板是非常模块化和复杂的,我很难弄清楚如何开始编写一个测试来模拟选择器和状态,以便我的 Saga 在测试中使用。

在 Saga 中,我使用 Reselect(在“./selectors”文件中)从 reducer 中获取“用户名”和“密码”,使用 yield select 将它们分配给常量,并使用它运行 API 调用。我认为我的问题不在于测试 saga,而是模拟一个状态和 saga 中的选择器来模拟登录信息已被填写并可以从状态中获取。

import { takeLatest, call, put, select } from 'redux-saga/effects';
import { loginSuccess, loginFailed } from '../App/actions';
import { ON_LOGIN } from '../App/constants';
import {
  makeSelectUsername,
  makeSelectPassword,
} from './selectors';
import api from '../../utils/api';

// LISTENER
export default function* loginRequestListener() {
  yield takeLatest(ON_LOGIN, login);
}

// WORKER
export function* login() {
  const loginParams = {
    username: yield select(makeSelectUsername()),
    password: yield select(makeSelectPassword()),
    isForceLoginAttempt: yield select(makeSelectIsForceLogin()),
  };

    try {
      const user = yield call(api.user.login, loginParams);
      yield put(loginSuccess(user));
    } catch (error) {
      yield put(loginFailed(error.response.data));
    }
}

基本上,我正在寻找一种在测试中运行 saga 的方法,以便在 saga 内部,我可以“强制”loginParams 对象使用“fakeState”供选择器使用,因为显然状态不'这里不存在:

const fakeState = {username: 'test', password: 'test'}

export function* login() {
  const loginParams = {
    username: yield select(makeSelectUsername()) // 'test'
    password: yield select(makeSelectPassword()) // 'test'
  };

我一直在将redux-saga-tester 作为测试传奇的一种手段,我只是想我不知道从哪里开始模拟测试中的状态,我的传奇可以将常量分配给...我想从那里我可以处理剩下的事情。

有没有人,特别是如果您使用过样板文件,有什么建议?

【问题讨论】:

    标签: javascript testing redux jestjs redux-saga


    【解决方案1】:

    我们在我们的应用程序中使用 redux-saga-tester,我们处理这个问题的方式不是通过传入一个假状态,我们使用 .next 方法,然后返回我们期望的特定测试的结果.请参阅下面的案例了解我所描述的内容。

    const activeOrderId = '1111';
    
    //returns the value for a isConnected boolean
      it.next('should get isConnected', (result) => {
        expect(result).toEqual(select(getIsConnected));
        return true;
      });
    
    // returns the value for an activeOrderId
      it.next('should get activeOrderId', (result) => {
        expect(result).toEqual(select(getActiveOrderId));
        return activeOrderId;
      });`
    

    这种方法的优点是您不必为所有 saga 定义一个大的“假状态”,并且您对每个 saga 的测试都与此分离。无论如何,您应该对选择器进行单独的测试,以确保它们正常工作。一般来说,saga 测试的一个痛点是,如果您的 saga 中有任何类型的控制流(if/else),那么完全测试所有内容的最佳策略似乎是为该 saga 编写一个完整的其他测试流。说你有这个

    isConnected = yield select(getIsConnected)
    if(isConnected) {
       // do x
    } else {
       // do y
    }
    

    我们已经通过编写两个描述来处理这个问题。一个我们将 isConnected 设置为 true(如上例所示),另一个我们将其设置为 false,如下所示

        //returns the value for a isConnected boolean
      it.next('should get isConnected', (result) => {
        expect(result).toEqual(select(getIsConnected));
        return false;
      });
    

    如果您需要将有效负载传递给 saga,可以使用 sagaHelper 函数来处理它,如下所示

      it.next = sagaHelper(login({
         username: 'testUsername',
         password: 'testPassword',
         // etc
      }));
    

    希望有帮助!

    【讨论】:

      【解决方案2】:

      redux-saga-test-plan 是最流行的用于测试 sagas 的库。

      http://redux-saga-test-plan.jeremyfairbank.com/

      【讨论】:

        猜你喜欢
        • 2017-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-08
        • 1970-01-01
        • 2017-09-14
        相关资源
        最近更新 更多