Как правильно тестировать лакомство? - PullRequest
3 голосов
/ 19 марта 2019

Я использую библиотеку 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)
}

Любой может понять, как я могу протестировать действия с избыточностью и т. Д. С обычным избыточным текстом я мог бы найти много статей, но с библиотекой избыточного соуса я, похоже, ничего не могу найти. Любые подсказки, пожалуйста?

1 Ответ

2 голосов
/ 24 марта 2019

Что тестируется

LanguageRedux.js имеет следующий экспорт:

  • LanguageTypes - карта типов действий
  • Creators - карта действий создателей
  • INITIAL_STATE - начальное состояние приложения
  • getLanguage и setLanguage - функции редуктора
  • reducer - редуктор

Я рекомендую импортировать все с ожидаемыми идентификаторами, например:

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

Примечание : похоже, что действие getLanguage не нужно, поскольку оно ничего не делает для состояния (если приложение получает язык, оно должно просто прочитать его из состояния), но я буду оставьте это там, так как оно находится в коде вопроса.


LanguageTypes

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

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 является редукторным редуктором, и его задача - направить действия в соответствующую функцию редуктора и вернуть полученное состояние:

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 запускается, как только код импортируется и захватывает ссылки на локальные функции setLanguage и getLanguage. Если вы хотите использовать этот подход, то самый простой способ сделать это - переместить reducer в его собственный модуль, чтобы можно было смоделировать функции редуктора перед импортом reducer в ваш тестовый код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...