无法使用 Jest 对具有选择器功能的 redux-saga 进行单元测试
Can't unit test redux-saga with selector function using Jest
问题解释:
我想使用 Jest 对 redux-saga 进行单元测试。我按照 redux-saga 文档中提供的示例中描述的方式执行此操作:https://redux-saga.js.org/docs/advanced/Testing.html
在我的 Saga 中,我正在调用一个选择器函数 selectSet
,它 return 是来自应用程序商店的特定对象:
export const selectSet = state => state.setStore.set
在我的故事中,我试图产生这个选择器函数:
import { put, select } from 'redux-saga/effects'
import { selectSet } from '../selectors'
export function* getSet() {
try {
const set = yield select(selectSet)
yield put({ type: 'SET_SUCCESS', payload: { set } })
} catch (error) {
yield put({ type: 'SET_ERROR', payload: { error } })
}
}
在我的测试中,没有有效的应用程序商店,因此我必须将函数模拟为 return 预期对象:
import assert from 'assert'
import * as AppRoutines from './AppRoutines'
import { put, select } from 'redux-saga/effects'
describe('getSet()', () => {
it('should trigger an action type "SET_SUCCESS" with a payload containing a valid set', () => {
const generator = AppRoutines.getSet()
const set = {
id: 1,
slots: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }],
}
const selectSet = jest.fn()
selectSet.mockReturnValue(set)
// Saga step 1
const actualFirst = generator.next().value
const expectedFirst = select(selectSet)
assert.deepEqual(
actualFirst,
expectedFirst,
'it should retreive a valid set from the store using the selectSet selector'
)
})
})
但是 - 如果我使用 deepEqual
和我的模拟函数将 saga 断言为 return 一个特定的生成器值,它期望我的选择器函数具有原始的 selectSet
构造函数。但是由于我用 jest.fn() 模拟函数,构造函数实际上等于 mockConstructor
- 这使我的测试失败:
Expected value to deeply equal to:
{"@@redux-saga/IO": true, "SELECT": {"args": Array [], "selector": [Function mockConstructor]}}
Received:
{"@@redux-saga/IO": true, "SELECT": {"args": Array [], "selector": [Function selectSet]}}
问题: 我如何制作一个 assert.deepEqual 包含模拟函数而不冲突的构造函数类型?
替代问题:有没有办法让我的断言期望 mockConstructor
而不是实际的 selectSet
构造函数?
您根本不需要模拟选择器,因为在这种性质的 saga 测试中,选择器从未真正被调用,而是测试为 redux saga 中间件创建的声明性指令以执行操作如你所料
这是 saga 将创建的指令 {"@@redux-saga/IO": true, "SELECT": {"args": Array [], "selector": [Function selectSet]}}
,但由于中间件不是 运行 在此测试场景中 selectSelect
永远不会真正被调用
如果您需要为您的操作模拟选择器 returns 的结果,那么您可以通过将模拟数据传递到下一步来实现...
const set = {
id: 1,
slots: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }],
}
// Saga step 1
const firstYield = generator.next().value
assertDeepEqual(firstYield, select(selectSet))
// Step 2 - successful so dispatch action
// mock data from the previous yield by passing into this step via `next(stubbedYieldedData)`
const secondYield = generator.next(set).value
assertDeepEqual(secondYield, put({type: 'SET_SUCCESS', payload: {set} }))
我们可以通过如下所示的假商店中的模拟商店。下面是示例选择器和生成器函数及其测试。
选择器
const authSelector = (state) => state.authReducer || initialState;
Saga 生成器函数
export function* getAuthToken(action) {
try {
const authToken = yield select(makeSelectAuthToken());
} catch (errObj) {}
}
测试用例
import { runSaga } from 'redux-saga'
const dispatchedActions = [];
const fakeStore = {
getState: () => ({ authReducer: { auth: 'test' } }),
dispatch: (action) => dispatchedActions.push(action)
}
await runSaga(fakeStore, getAuthToken, {
payload: {}
}).done;
expected case you can write here below this
问题解释:
我想使用 Jest 对 redux-saga 进行单元测试。我按照 redux-saga 文档中提供的示例中描述的方式执行此操作:https://redux-saga.js.org/docs/advanced/Testing.html
在我的 Saga 中,我正在调用一个选择器函数 selectSet
,它 return 是来自应用程序商店的特定对象:
export const selectSet = state => state.setStore.set
在我的故事中,我试图产生这个选择器函数:
import { put, select } from 'redux-saga/effects'
import { selectSet } from '../selectors'
export function* getSet() {
try {
const set = yield select(selectSet)
yield put({ type: 'SET_SUCCESS', payload: { set } })
} catch (error) {
yield put({ type: 'SET_ERROR', payload: { error } })
}
}
在我的测试中,没有有效的应用程序商店,因此我必须将函数模拟为 return 预期对象:
import assert from 'assert'
import * as AppRoutines from './AppRoutines'
import { put, select } from 'redux-saga/effects'
describe('getSet()', () => {
it('should trigger an action type "SET_SUCCESS" with a payload containing a valid set', () => {
const generator = AppRoutines.getSet()
const set = {
id: 1,
slots: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }],
}
const selectSet = jest.fn()
selectSet.mockReturnValue(set)
// Saga step 1
const actualFirst = generator.next().value
const expectedFirst = select(selectSet)
assert.deepEqual(
actualFirst,
expectedFirst,
'it should retreive a valid set from the store using the selectSet selector'
)
})
})
但是 - 如果我使用 deepEqual
和我的模拟函数将 saga 断言为 return 一个特定的生成器值,它期望我的选择器函数具有原始的 selectSet
构造函数。但是由于我用 jest.fn() 模拟函数,构造函数实际上等于 mockConstructor
- 这使我的测试失败:
Expected value to deeply equal to:
{"@@redux-saga/IO": true, "SELECT": {"args": Array [], "selector": [Function mockConstructor]}}
Received:
{"@@redux-saga/IO": true, "SELECT": {"args": Array [], "selector": [Function selectSet]}}
问题: 我如何制作一个 assert.deepEqual 包含模拟函数而不冲突的构造函数类型?
替代问题:有没有办法让我的断言期望 mockConstructor
而不是实际的 selectSet
构造函数?
您根本不需要模拟选择器,因为在这种性质的 saga 测试中,选择器从未真正被调用,而是测试为 redux saga 中间件创建的声明性指令以执行操作如你所料
这是 saga 将创建的指令 {"@@redux-saga/IO": true, "SELECT": {"args": Array [], "selector": [Function selectSet]}}
,但由于中间件不是 运行 在此测试场景中 selectSelect
永远不会真正被调用
如果您需要为您的操作模拟选择器 returns 的结果,那么您可以通过将模拟数据传递到下一步来实现...
const set = {
id: 1,
slots: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }],
}
// Saga step 1
const firstYield = generator.next().value
assertDeepEqual(firstYield, select(selectSet))
// Step 2 - successful so dispatch action
// mock data from the previous yield by passing into this step via `next(stubbedYieldedData)`
const secondYield = generator.next(set).value
assertDeepEqual(secondYield, put({type: 'SET_SUCCESS', payload: {set} }))
我们可以通过如下所示的假商店中的模拟商店。下面是示例选择器和生成器函数及其测试。
选择器
const authSelector = (state) => state.authReducer || initialState;
Saga 生成器函数
export function* getAuthToken(action) {
try {
const authToken = yield select(makeSelectAuthToken());
} catch (errObj) {}
}
测试用例
import { runSaga } from 'redux-saga'
const dispatchedActions = [];
const fakeStore = {
getState: () => ({ authReducer: { auth: 'test' } }),
dispatch: (action) => dispatchedActions.push(action)
}
await runSaga(fakeStore, getAuthToken, {
payload: {}
}).done;
expected case you can write here below this