如何测试异步动作?
How to test asynchronous action?
我有一个动作:
export const GetChatList = userStatus => {
return dispatch => {
dispatch({
type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST,
payload: {}
});
axios
.get(config.apiUrl + config.methods.getMessagesList, { params: { accountType: userStatus } })
.then(res => {
dispatch({
type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS,
payload: res.data
});
})
.catch(err => {
dispatch({
type: MessagesActionTypes.GET_MESSAGES_LIST.ERROR,
payload: 'error text'
});
});
};
};
我试着为这个动作写了一个测试:
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Messages actions', () => {
afterEach(() => {
fetchMock.restore();
});
it('GetChatList', () => {
fetchMock.get(config.apiUrl + config.methods.getMessagesList, { params: { accountType: 1 } });
const expectedActions = [
{ type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST },
{
type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS,
payload: ...somePayload
},
{
type: MessagesActionTypes.GET_MESSAGES_LIST.ERROR,
payload: 'error text'
}
];
const store = mockStore({...initialState});
return store.dispatch(GetChatList(1)).then(() => expect(store.getActions()).toEqual(expectedActions));
});
});
然后我得到一个错误:TypeError: Cannot read 属性 'then' of undefined
为什么会发生这种情况以及如何正确测试此操作?我的错误是什么?
fetch-mock
模拟使用 fetch
发出的 HTTP 请求。但是你正在使用 axios
.
你应该 return 在 thunk 中由 axios.get()
创建的承诺。这样你就可以调用store.dispatch(GetChatList(1)).then()
方法。
您可以使用 jest.spyOn(axios, 'get')
模拟 axios.get()
方法及其 resolved/rejected 值。
例如
thunk.ts
:
import axios from 'axios';
export const MessagesActionTypes = {
GET_MESSAGES_LIST: {
REQUEST: 'REQUEST',
SUCCESS: 'SUCCESS',
ERROR: 'ERROR',
},
};
const config = {
apiUrl: 'http://localhost:8080/v1/api',
methods: {
getMessagesList: '/messages',
},
};
export const GetChatList = (userStatus) => {
return (dispatch) => {
dispatch({ type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST, payload: {} });
return axios
.get(config.apiUrl + config.methods.getMessagesList, { params: { accountType: userStatus } })
.then((res) => dispatch({ type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS, payload: res.data }))
.catch((err) => dispatch({ type: MessagesActionTypes.GET_MESSAGES_LIST.ERROR, payload: 'error text' }));
};
};
thunk.test.ts
:
import { GetChatList, MessagesActionTypes } from './thunk';
import configureStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import axios from 'axios';
interface AppState {}
type DispatchExts = ThunkDispatch<AppState, void, AnyAction>;
const mws = [thunk];
const mockStore = configureStore<AppState, DispatchExts>(mws);
describe('71296970', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('should pass', () => {
jest.spyOn(axios, 'get').mockResolvedValue({ data: 'fake data' });
const store = mockStore({});
const expectedActions = [
{ type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST, payload: {} },
{ type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS, payload: 'fake data' },
];
return store.dispatch(GetChatList(1)).then(() => {
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
});
测试结果:
PASS Whosebug/71296970/thunk.test.ts
71296970
✓ should pass (5 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
thunk.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.383 s
我有一个动作:
export const GetChatList = userStatus => {
return dispatch => {
dispatch({
type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST,
payload: {}
});
axios
.get(config.apiUrl + config.methods.getMessagesList, { params: { accountType: userStatus } })
.then(res => {
dispatch({
type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS,
payload: res.data
});
})
.catch(err => {
dispatch({
type: MessagesActionTypes.GET_MESSAGES_LIST.ERROR,
payload: 'error text'
});
});
};
};
我试着为这个动作写了一个测试:
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Messages actions', () => {
afterEach(() => {
fetchMock.restore();
});
it('GetChatList', () => {
fetchMock.get(config.apiUrl + config.methods.getMessagesList, { params: { accountType: 1 } });
const expectedActions = [
{ type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST },
{
type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS,
payload: ...somePayload
},
{
type: MessagesActionTypes.GET_MESSAGES_LIST.ERROR,
payload: 'error text'
}
];
const store = mockStore({...initialState});
return store.dispatch(GetChatList(1)).then(() => expect(store.getActions()).toEqual(expectedActions));
});
});
然后我得到一个错误:TypeError: Cannot read 属性 'then' of undefined 为什么会发生这种情况以及如何正确测试此操作?我的错误是什么?
fetch-mock
模拟使用fetch
发出的 HTTP 请求。但是你正在使用axios
.你应该 return 在 thunk 中由
axios.get()
创建的承诺。这样你就可以调用store.dispatch(GetChatList(1)).then()
方法。您可以使用
jest.spyOn(axios, 'get')
模拟axios.get()
方法及其 resolved/rejected 值。
例如
thunk.ts
:
import axios from 'axios';
export const MessagesActionTypes = {
GET_MESSAGES_LIST: {
REQUEST: 'REQUEST',
SUCCESS: 'SUCCESS',
ERROR: 'ERROR',
},
};
const config = {
apiUrl: 'http://localhost:8080/v1/api',
methods: {
getMessagesList: '/messages',
},
};
export const GetChatList = (userStatus) => {
return (dispatch) => {
dispatch({ type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST, payload: {} });
return axios
.get(config.apiUrl + config.methods.getMessagesList, { params: { accountType: userStatus } })
.then((res) => dispatch({ type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS, payload: res.data }))
.catch((err) => dispatch({ type: MessagesActionTypes.GET_MESSAGES_LIST.ERROR, payload: 'error text' }));
};
};
thunk.test.ts
:
import { GetChatList, MessagesActionTypes } from './thunk';
import configureStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import axios from 'axios';
interface AppState {}
type DispatchExts = ThunkDispatch<AppState, void, AnyAction>;
const mws = [thunk];
const mockStore = configureStore<AppState, DispatchExts>(mws);
describe('71296970', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('should pass', () => {
jest.spyOn(axios, 'get').mockResolvedValue({ data: 'fake data' });
const store = mockStore({});
const expectedActions = [
{ type: MessagesActionTypes.GET_MESSAGES_LIST.REQUEST, payload: {} },
{ type: MessagesActionTypes.GET_MESSAGES_LIST.SUCCESS, payload: 'fake data' },
];
return store.dispatch(GetChatList(1)).then(() => {
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
});
测试结果:
PASS Whosebug/71296970/thunk.test.ts
71296970
✓ should pass (5 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
thunk.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.383 s