Есть ли идиоматический способ проверки вложенных ветвей штата?
Итак, скажем, что у меня есть редуктор с несколькими ветвями, но каждая ветвь достаточно похожа на генератор с функцией factory. Итак, я создаю один:
import { combineReducers } from 'redux'
const createReducerScope = scopeName => {
const scope = scopeName.toUpperCase()
const contents = (state = {}, action) => {
switch (action.type) {
case `${scope}_INSERT`:
return { ...state, [action.id]: action.item }
default:
return state
}
}
const meta = (state = {}, action) => {
switch (action.type) {
case `${scope}_REQUEST`:
return { ...state, requesting: true }
case `${scope}_REQUEST_FAILURE`:
return {
...state,
requesting: false,
errorMessage: String(action.error)
}
case `${scope}_REQUEST_SUCCESS`:
return {
...state,
requesting: false,
errorMessage: null
}
default:
return state
}
}
return combineReducers({ contents, meta })
}
Что я использую для компоновки большего дерева состояний корневого уровня:
const products = createReducerScope('products')
const orders = createReducerScope('orders')
const trades = createReducerScope('trades')
const rootReducer = combineReducers({ products, orders, trades })
Это должно дать мне граф состояний, который будет выглядеть следующим образом:
{
products: { contents, meta },
orders: { contents, meta },
trades: { contents, meta }
}
Если бы я хотел протестировать это состояние, моим первым инстинктом является создание версии этой области в моем тестовом наборе, а затем проверка этого изолированного редуктора (только утверждение против ветвей contents
и meta
).
Усложнение заключается в том, что я пытаюсь также тестировать селектор, и все созданные мной селекторные схемы, кажется, предлагают эти две вещи:
- Вы инкапсулируете свое состояние, выбирая селектор и редукторы в том же файле.
-
mapStateToProps
нужно знать только две вещи: глобальное состояние и соответствующий селектор.
Итак, вот моя проблема: Сопряжение этих более или менее сложных редукторов с селекторами, зависящими от уровня корня, сделало мои тесты немного толстыми с шаблоном.
Не говоря уже о том, что записывающие селектора со знанием всего дерева чувствуют, что это бросает вызов точке попытки модульной редукции.
Я на 100% уверен, что мне не хватает чего-то очевидного, но я не могу найти какой-либо примерный код, который демонстрирует способ тестирования модульных редукторов и селекторов.
Если селектор, как правило, должен знать все глобальное состояние, но у вас есть редукторы, которые очень сложны, есть ли чистый, идиоматический подход к тестированию? Или, может быть, более сложный селекторный дизайн?
Ответы
Ответ 1
Точка выделения селекторов и редукторов редукторы, а селекторы в одном файле должны работать в одной и той же форме состояния. Если вы разделите редукторы на несколько файлов для их компоновки, вы должны сделать то же самое для своих селекторов.
Вы можете увидеть пример этого в моей новой серии Egghead (видео 10 и 20 может быть особенно полезно).
Итак, ваш код должен быть больше похож на
const createList = (type) => {
const contents = ...
const meta = ...
return combineReducers({ contents, meta })
}
// Use default export for your reducer
// or for a reducer factory function.
export default createList
// Export associated selectors
// as named exports.
export const getIsRequesting = (state) => ...
export const getErrorMessage = (state) => ...
Затем ваш index.js
может выглядеть как
import createList, * as fromList from './createList'
const products = createList('products')
const orders = createList('orders')
const trades = createList('trades')
export default combineReducers({ products, orders, trades })
export const getIsFetching = (state, type) =>
fromList.getIsFetching(state[type])
export const getErrorMessage = (state, type) =>
fromList.getErrorMessage(state[type])
Таким образом, корневые селектора делегируют дочерние селекторы, так же как и корневые редукторы делегируют дочерние редукторы. В каждом файле state
внутри селекторов соответствует state
экспортированного редуктора, а детали реализации формы состояния не распространяются на другие файлы.
Наконец, для тестирования этих пакетов редуктора/селектора вы можете сделать что-то вроде
import createList, * as fromList from './createList')
describe('createList', () => {
it('is not requesting initially', () => {
const list = createList('foo')
const state = [{}].reduce(list)
expect(
fromList.isRequesting(state)
).toBe(false)
})
it('is requesting after *_REQUEST', () => {
const list = createList('foo')
const state = [{}, { type: 'foo_REQUEST' }].reduce(list)
expect(
fromList.isRequesting(state)
).toBe(true)
})
})