测试调用 API 的 redux 操作
Posted
技术标签:
【中文标题】测试调用 API 的 redux 操作【英文标题】:Test redux actions that calls an API 【发布时间】:2016-02-21 01:10:16 【问题描述】:测试此功能的最佳方法是什么
export function receivingItems()
return (dispatch, getState) =>
axios.get('/api/items')
.then(function(response)
dispatch(receivedItems(response.data));
);
;
这是我目前拥有的
describe('Items Action Creator', () =>
it('should create a receiving items function', () =>
expect(receivingItems()).to.be.a.function;
);
);
【问题讨论】:
【参考方案1】:来自 Redux “Writing Tests” 配方:
对于使用 Redux Thunk 或其他中间件的异步操作创建者,最好完全模拟 Redux 存储进行测试。您仍然可以将
applyMiddleware()
与模拟商店一起使用,如下所示(您可以在redux-mock-store 中找到以下代码)。您还可以使用 nock 模拟 HTTP 请求。function fetchTodosRequest() return type: FETCH_TODOS_REQUEST function fetchTodosSuccess(body) return type: FETCH_TODOS_SUCCESS, body function fetchTodosFailure(ex) return type: FETCH_TODOS_FAILURE, ex export function fetchTodos() return dispatch => dispatch(fetchTodosRequest()) return fetch('http://example.com/todos') .then(res => res.json()) .then(json => dispatch(fetchTodosSuccess(json.body))) .catch(ex => dispatch(fetchTodosFailure(ex)))
可以像这样测试:
import expect from 'expect' import applyMiddleware from 'redux' import thunk from 'redux-thunk' import * as actions from '../../actions/counter' import * as types from '../../constants/ActionTypes' import nock from 'nock' const middlewares = [ thunk ] /** * Creates a mock of Redux store with middleware. */ function mockStore(getState, expectedActions, done) if (!Array.isArray(expectedActions)) throw new Error('expectedActions should be an array of expected actions.') if (typeof done !== 'undefined' && typeof done !== 'function') throw new Error('done should either be undefined or function.') function mockStoreWithoutMiddleware() return getState() return typeof getState === 'function' ? getState() : getState , dispatch(action) const expectedAction = expectedActions.shift() try expect(action).toEqual(expectedAction) if (done && !expectedActions.length) done() return action catch (e) done(e) const mockStoreWithMiddleware = applyMiddleware( ...middlewares )(mockStoreWithoutMiddleware) return mockStoreWithMiddleware() describe('async actions', () => afterEach(() => nock.cleanAll() ) it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', (done) => nock('http://example.com/') .get('/todos') .reply(200, todos: ['do something'] ) const expectedActions = [ type: types.FETCH_TODOS_REQUEST , type: types.FETCH_TODOS_SUCCESS, body: todos: ['do something'] ] const store = mockStore( todos: [] , expectedActions, done) store.dispatch(actions.fetchTodos()) ) )
【讨论】:
另外请记住,createMockStore
已作为包发布:github.com/arnaudbenard/redux-mock-store
您可以使用github.com/wix/redux-testkit同步测试异步操作
嗨,如果正在测试的操作是getTodos
并且返回的数据是一个巨大的待办事项列表怎么办?你如何模拟这种情况?【参考方案2】:
我会使用一个存根axios
(例如使用mock-require)并编写一个实际调用receivingItems()(dispatch, getState)
的测试,并确保使用正确的数据调用dispatch
。
【讨论】:
【参考方案3】:我以不同的方式解决了这个问题:将 axios 作为操作的依赖项注入。我更喜欢这种方法而不是“重新布线”依赖项。
所以我使用了相同的方法来测试与 redux 连接的组件。当我导出操作时,我会导出两个版本:一个带有(用于组件)和一个不带(用于测试)绑定依赖项。
这是我的 actions.js 文件的样子:
import axios from 'axios'
export const loadDataRequest = () =>
return
type: 'LOAD_DATA_REQUEST'
export const loadDataError = () =>
return
type: 'LOAD_DATA_ERROR'
export const loadDataSuccess = (data) =>
return
type: 'LOAD_DATA_SUCCESS',
data
export const loadData = (axios) =>
return dispatch =>
dispatch(loadDataRequest())
axios
.get('http://httpbin.org/ip')
.then((data)=> dispatch(loadDataSuccess(data)))
.catch(()=> dispatch(loadDataError()))
export default
loadData: loadData.bind(null, axios)
然后使用jest (actions.test.js) 进行测试:
import loadData from './actions'
describe('testing loadData', ()=>
test('loadData with success', (done)=>
const get = jest.fn()
const data =
mydata: test: 1
get.mockReturnValue(Promise.resolve(data))
let callNumber = 0
const dispatch = jest.fn(params =>
if (callNumber===0)
expect(params).toEqual( type: 'LOAD_DATA_REQUEST' )
if (callNumber===1)
expect(params).toEqual(
type: 'LOAD_DATA_SUCCESS',
data: data
)
done()
callNumber++
)
const axiosMock =
get
loadData(axiosMock)(dispatch)
)
)
在组件中使用操作时,我会导入所有内容:
import Actions from './actions'
并发送:
Actions.loadData() // this is the version with axios binded.
【讨论】:
以上是关于测试调用 API 的 redux 操作的主要内容,如果未能解决你的问题,请参考以下文章
React + Redux App:通过 Redux 操作调用 API Vs 直接调用 API
在 React Redux 中调度操作之前等待多个 Axios 调用