6

redux ストアに reduxsauce ライブラリを使用しています。その中の単一の redux ストアを単体テストしたいと考えています。還元ファイル:

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)

今、別のファイルでストアを構成していますが、リデューサーは別のファイルに結合されています。

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 ライブラリでは何も見つからないようです。手がかりはありますか?

4

1 に答える 1

6

テスト対象

LanguageRedux.js次のエクスポートがあります。

  • LanguageTypes- アクションタイプのマップ
  • Creators- アクションクリエーターのマップ
  • INITIAL_STATE- アプリの初期状態
  • getLanguageそしてsetLanguage- レデューサー関数
  • reducer- redux レデューサー

次のような予想される識別子を使用してすべてをインポートすることをお勧めします。

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

:状態に対して何もしないため(アプリが言語を取得している場合は、状態から読み取るだけでよい)、アクションは不要のように見えgetLanguageますが、質問コードにあるため、そこに残します。


言語の種類

LanguageTypesは、関連付けられた文字列値へのアクション タイプの単なるマップです。

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

クリエイター

Creatorsアクションクリエーターのマップです。

各アクション クリエーターは、指定されたパラメーターに基づいてアクション オブジェクトを生成する純粋な関数です。

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アプリが開始する初期状態オブジェクトです。

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

レデューサー機能

getLanguageつまり、与えられた既存の状態とsetLanguageアクションに基づいて新しい状態を返す純粋な関数です。

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呼び出されるため、標準のreduxレデューサーをテストするよりもさらに簡単であることに注意してください。


減速機

reducerは redux レデューサーであり、その仕事はアクションを対応するレデューサー関数にルーティングし、結果の状態を返すことです。

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:テストできる方法はいくつかあります。上記のアプローチは、リデューサー関数を介して状態とアクションをすべて渡します。徹底していますが、上記のレデューサー関数のテストと多くの重複があります。

最も基本的な代替手段は、スパイして、期待されるマッピングオブジェクトcreateReducerで呼び出されたことを単純に確認することです。INITIAL_STATE

それと上記の完全なアプローチの中間にあるアプローチは、レデューサー関数をモックし、reducerさまざまなアクションを渡し、正しいレデューサー関数が呼び出されたことを確認することです。これはおそらく理想的なアプローチですが、コードがインポートされるとすぐに実行され、ローカル関数およびcreateReducerへの参照がキャプチャされるため、現在のコードの記述方法を実装することは困難です。このアプローチを使用したい場合、最も簡単な方法は独自のモジュールに移動して、テスト コードにインポートする前にレデューサー関数をモックできるようにすることです。setLanguagegetLanguagereducerreducer

于 2019-03-24T08:03:59.437 に答える