如何对 reduxsauce 进行单元测试? [英] How to unit test reduxsauce?

查看:28
本文介绍了如何对 reduxsauce 进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将 reduxsauce 库用于 redux 存储,我想对其中的单个 redux 存储进行单元测试.redux 文件:

I am using reduxsauce library for redux store, and I want to unit test a single redux store in it. The redux file:

import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  getLanguage: [],
  setLanguage: ['language']
})

export const LanguageTypes = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  language: "en"
})

/* ------------- Reducers ------------- */


export const getLanguage = (state: Object, {}: Object) => {
    return state.merge({})
}

export const setLanguage = (state: Object, { language }: Object) => {
    return state.merge({ language })
}

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.SET_LANGUAGE]: setLanguage,
  [Types.GET_LANGUAGE]: getLanguage,
})

测试:

import * as actions from '../../../redux/LanguageRedux'
import * as types from '../../../redux/LanguageRedux'

describe('Language redux ', () => {
  it('should have default language ', () => {
    expect(actions.INITIAL_STATE.language).toEqual("en")
  }),
  it('should be able to set the language', () => {
    // I know the calls below are not tests but still its relevant with the error
    actions.getLanguage()
    actions.setLanguage()
  })
})

错误:

● Language redux  › should be able to set the language

    TypeError: Cannot destructure 'undefined' or 'null'.

      21 |
      22 |
    > 23 | export const getLanguage = (state: Object, {}: Object) => {
         |                            ^
      24 |     return state.merge({})
      25 | }
      26 |

      at Object.getLanguage (src/redux/LanguageRedux.js:23:28)
      at Object.getLanguage (src/__tests__/src/redux/LanguageRedux.js:9:13)

现在,我在不同的文件中配置了存储,但减速器组合在不同的文件中:

Now, I have the store configured in a different file but reducers is combined in a different file:

import { combineReducers } from 'redux'
import configureStore from './CreateStore'

import rootSaga from '../sagas'

export default () => {
    /* ------------- Assemble The Reducers ------------- */
    const rootReducer = combineReducers({
        language: require('./LanguageRedux').reducer
    })

    return configureStore(rootReducer, rootSaga)
}

任何人都知道我如何测试 redux 操作等.使用普通的 redux 我可以找到很多文章,但是使用 reduxsauce 库我似乎找不到任何东西.请问有什么线索吗?

Any one has a clue as to how could I test the redux actions etc. With normal redux I could find many articles but with reduxsauce library I cant seem to find anything. Any clues please?

推荐答案

正在测试什么

LanguageRedux.js 有以下导出:

  • LanguageTypes - 操作类型的映射
  • Creators - 动作创建者的地图
  • INITIAL_STATE - 应用的初始状态
  • getLanguagesetLanguage - reducer 函数
  • reducer - redux reducer
  • LanguageTypes - a map of the action types
  • Creators - a map of the action creators
  • INITIAL_STATE - the initial state of the app
  • getLanguage and setLanguage - the reducer functions
  • reducer - the redux reducer

我建议像这样导入具有预期标识符的所有内容:

I recommend importing everything with the expected identifiers like this:

import Creators, {
  LanguageTypes,
  INITIAL_STATE,
  getLanguage,
  setLanguage,
  reducer
 } from '../../../redux/LanguageRedux';

注意:看起来 getLanguage 操作是不必要的,因为它对状态没有任何作用(如果应用程序正在获取语言,它应该只是从状态中读取它),但我会把它留在那里,因为它在问题代码中.

Note: It looks like the getLanguage action is unnecessary since it does nothing to the state (if the app is getting the language it should just read it from the state), but I'll leave it in there since it is in the question code.

LanguageTypes 只是操作类型与其关联字符串值的映射:

LanguageTypes is just a map of the action types to their associated string value:

it('should export the expected action types', () => {
  expect(LanguageTypes).toEqual({
    GET_LANGUAGE: 'GET_LANGUAGE',
    SET_LANGUAGE: 'SET_LANGUAGE'
  });  // Success!
});

<小时>

创作者

Creators 是动作创建者的地图.


Creators

Creators is a map of the action creators.

每个动作创建者都是一个纯函数,它根据给定的参数生成一个动作对象:

Each action creator is a pure function that generates an action object based on the parameters given:

describe('Creators', () => {

  describe('getLanguage', () => {

    it('should return the expected action', () => {
      expect(Creators.getLanguage()).toEqual({
        type: LanguageTypes.GET_LANGUAGE
      });
    });

    it('should ignore extra args', () => {
      expect(Creators.getLanguage('extra arg')).toEqual({
        type: LanguageTypes.GET_LANGUAGE
      });
    });

  });

  describe('setLanguage', () => {

    it('should return the expected action when passed nothing', () => {
      expect(Creators.setLanguage()).toEqual({
        type: LanguageTypes.SET_LANGUAGE
      });  // Success!
    });

    it('should return the expected action when passed a language', () => {
      expect(Creators.setLanguage('en')).toEqual({
        type: LanguageTypes.SET_LANGUAGE,
        language: 'en'
      });  // Success!
    });

    it('should ignore extra args', () => {
      expect(Creators.setLanguage('es', 'extra arg')).toEqual({
        type: LanguageTypes.SET_LANGUAGE,
        language: 'es'
      });  // Success!
    });

  });

});

<小时>

INITIAL_STATE

INITIAL_STATE 只是应用程序开始的初始状态对象:


INITIAL_STATE

INITIAL_STATE is simply the initial state object the app starts with:

it('should set the initial state ', () => {
  expect(INITIAL_STATE).toEqual({ language: "en" });  // Success!
});

<小时>

Reducer 函数

getLanguagesetLanguage 是 reducer 函数,这意味着它们是纯函数,它们根据现有状态和给定的动作返回新状态:


Reducer functions

getLanguage and setLanguage are reducer functions, meaning they are pure functions that return a new state based on the existing state and action they are given:

describe('reducers', () => {

  describe('getLanguage', () => {

    it('should do nothing (probably should not be an action)', () => {
      expect(getLanguage(INITIAL_STATE, {})).toEqual(INITIAL_STATE);  // Success!
    });

    it('should ignore extra args', () => {
      expect(getLanguage(INITIAL_STATE, { extra: 'arg' })).toEqual(INITIAL_STATE);  // Success!
    });

  });

  describe('setLanguage', () => {

    it('should set the language', () => {
      expect(setLanguage(INITIAL_STATE, { language: 'es' })).toEqual({
        language: 'es'
      });  // Success!
    });

    it('should ignore extra args', () => {
      expect(setLanguage(INITIAL_STATE, { language: 'fr', extra: 'arg' })).toEqual({
        language: 'fr'
      });  // Success!
    });

  });

});

请注意,使用 reduxsauce 测试 reducer 函数比测试标准的 redux reducer 更容易,因为它们会被设计的操作调用处理.

Note that testing reducer functions with reduxsauce is even easier than testing standard redux reducers since they will only be called for actions they are designed to handle.

reducer 是 redux reducer,它的工作是将 action 路由到相应的 reducer 函数并返回结果状态:

reducer is the redux reducer and its job is to route actions to the corresponding reducer function and return the resulting state:

describe('reducer', () => {

  it('should return initial state if passed nothing', () => {
    expect(reducer()).toEqual(INITIAL_STATE);  // Success!
  });

  it('should route GET_LANGUAGE to getLanguage', () => {
    expect(reducer(INITIAL_STATE, Creators.getLanguage())).toEqual(INITIAL_STATE);  // Success!
  });

  it('should route SET_LANGUAGE to setLanguage', () => {
    expect(reducer(Immutable({ language: 'es' }), Creators.setLanguage('fr'))).toEqual({
      language: 'fr'
    });  // Success!
  });

});

注意:有几种不同的方法可以测试 reducer.上述方法通过 reducer 函数一直传递状态和动作.很透彻,但也和上面的reducer功能测试有很多重叠.

Note: there are a few different ways that reducer can be tested. The above approach passes the state and actions all the way through the reducer functions. It is thorough, but also has a lot of overlap with the reducer function tests above.

最基本的替代方法是监视 createReducer 并简单地验证它是否是使用预期的 INITIAL_STATE 和映射对象调用的.

The most basic alternative is to spy on createReducer and simply verify that it was called with the expected INITIAL_STATE and mapping object.

介于上述方法和完整方法之间的方法是模拟 reducer 函数,传递 reducer 各种操作,并验证是否调用了正确的 reducer 函数.这可能是理想的方法,但很难实现当前编写代码的方式,因为 createReducer 会在导入代码后立即运行并捕获对本地函数的引用setLanguagegetLanguage.如果您想使用这种方法,那么最简单的方法是将 reducer 移动到它自己的模块中,这样您就可以在导入 reducer 之前 模拟 reducer 函数 到您的测试代码中.

The approach halfway between that and the full approach above is to mock the reducer functions, pass reducer various actions, and verify that the correct reducer function was called. This is probably the ideal approach but it is difficult to implement the way the code is currently written since createReducer runs as soon as the code is imported and captures references to the local functions setLanguage and getLanguage. If you wanted to use this approach then the easiest way to do it would be to move reducer to its own module so you could mock the reducer functions before importing reducer into your test code.

这篇关于如何对 reduxsauce 进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆