在 redux 动作测试中出错
err in a redux action test
我是 Jest 测试和 moxios 的新手。只是想写我的第一个异步动作测试。测试死于此错误:
Expected value to equal:
[{"payload": {"checked": true, "followingInfoId": "1"}, "type": "HANDLE_FAVORITE_SUCCESS"}]
Received:
[{"payload": [TypeError: Cannot read property 'getItem' of undefined], "type": "ERROR"}]
谁能告诉我问题出在哪里。我想 moxios 响应不会转到 "then"?
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moxios from 'moxios';
import * as actions from './index';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const store = mockStore();
describe('followings actions', () => {
beforeEach(() => {
moxios.install();
store.clearActions();
});
afterEach(() => {
moxios.uninstall();
});
it('dispatches the HANDLE_FAVORITE_SUCCESS action', () => {
moxios.wait(() => {
const request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
payload: {
followingInfoId: '1',
checked: true
}
});
});
const expectedActions = [
{
'type': 'HANDLE_FAVORITE_SUCCESS',
payload: {
followingInfoId: '1',
checked: true
}
}
];
return store.dispatch(actions.handleFavorite()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
这是动作创建者:
export const handleFavorite = data => {
return dispatch => {
return followApi.handleFavorite(data).then(payload => {
dispatch({ type: 'HANDLE_FAVORITE_SUCCESS', payload });
}, err => {
dispatch({ type: 'ERROR', payload: err })
});
}
};
这里是 followApi.handleFavorite:
handleFavorite: (data) => {
return new Promise ((resolve, reject) => {
httpServise.patch(`${host}:${port}/followings/handle-favorite`, data).then(
res => {
if (res.data.payload) {
resolve(res.data.payload);
} else reject({status: 401});
}, err => reject(err)
);
});
},
如果需要,还有 http 服务的一部分:
patch: (url, params) => {
return new Promise((resolve, reject) => {
axios(url, {
method: 'PATCH',
headers: getHeaders(),
data: params
}).then(res => {
resolve(res);
}, err => {
reject(err);
});
});
}
如果你想测试动作创建者,你应该模拟 followApi.handleFavorite
方法而不是 axios
。
这里是测试动作创建者只使用jestjs
和typescript
的解决方案,您可以自己手动模拟模块。
文件夹结构:
.
├── actionCreators.spec.ts
├── actionCreators.ts
├── followApi.ts
└── httpServise.ts
actionCreators.ts
:
import followApi from './followApi';
export const handleFavorite = data => {
return dispatch => {
return followApi.handleFavorite(data).then(
payload => {
dispatch({ type: 'HANDLE_FAVORITE_SUCCESS', payload });
},
err => {
dispatch({ type: 'ERROR', payload: err });
}
);
};
};
followApi.ts
:
import { httpServise } from './httpServise';
const host = 'http://github.com/mrdulin';
const port = 3000;
const followApi = {
handleFavorite: data => {
return new Promise((resolve, reject) => {
httpServise.patch(`${host}:${port}/followings/handle-favorite`, data).then(
(res: any) => {
if (res.data.payload) {
resolve(res.data.payload);
} else {
reject({ status: 401 });
}
},
err => reject(err)
);
});
}
};
export default followApi;
httpService.ts
:
import axios from 'axios';
function getHeaders() {
return {};
}
export const httpServise = {
patch: (url, params) => {
return new Promise((resolve, reject) => {
axios(url, {
method: 'PATCH',
headers: getHeaders(),
data: params
}).then(
res => {
resolve(res);
},
err => {
reject(err);
}
);
});
}
};
actionCreators.spec.ts
:
import configureMockStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import * as actions from './actionCreators';
import followApi from './followApi';
jest.mock('./followApi.ts', () => {
return {
handleFavorite: jest.fn()
};
});
type State = any;
const middlewares = [thunk];
const mockStore = configureMockStore<State, ThunkDispatch<State, undefined, AnyAction>>(middlewares);
const store = mockStore();
describe('followings actions', () => {
beforeEach(() => {
store.clearActions();
jest.resetAllMocks();
});
it('dispatches the HANDLE_FAVORITE_SUCCESS action', () => {
expect.assertions(2);
const mockedHandleFavoritePayload = {
followingInfoId: '1',
checked: true
};
(followApi.handleFavorite as jest.MockedFunction<typeof followApi.handleFavorite>).mockResolvedValueOnce(
mockedHandleFavoritePayload
);
const data = 'jest';
const expectedActions = [
{
type: 'HANDLE_FAVORITE_SUCCESS',
payload: {
followingInfoId: '1',
checked: true
}
}
];
return store.dispatch(actions.handleFavorite(data)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(followApi.handleFavorite).toBeCalledWith(data);
});
});
it('dispatches the ERROR action', () => {
const mockedhHndleFavoriteError = new Error('network error');
(followApi.handleFavorite as jest.MockedFunction<typeof followApi.handleFavorite>).mockRejectedValueOnce(
mockedhHndleFavoriteError
);
const data = 'jest';
const expectedActions = [
{
type: 'ERROR',
payload: mockedhHndleFavoriteError
}
];
return store.dispatch(actions.handleFavorite(data)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(followApi.handleFavorite).toBeCalledWith(data);
});
});
});
具有 100% 覆盖率报告的单元测试结果:
PASS src/Whosebug/52025257/actionCreators.spec.ts (5.95s)
followings actions
✓ dispatches the HANDLE_FAVORITE_SUCCESS action (5ms)
✓ dispatches the ERROR action (2ms)
-------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
actionCreators.ts | 100 | 100 | 100 | 100 | |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 6.87s, estimated 7s
源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/Whosebug/52025257
我是 Jest 测试和 moxios 的新手。只是想写我的第一个异步动作测试。测试死于此错误:
Expected value to equal:
[{"payload": {"checked": true, "followingInfoId": "1"}, "type": "HANDLE_FAVORITE_SUCCESS"}]
Received:
[{"payload": [TypeError: Cannot read property 'getItem' of undefined], "type": "ERROR"}]
谁能告诉我问题出在哪里。我想 moxios 响应不会转到 "then"?
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moxios from 'moxios';
import * as actions from './index';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const store = mockStore();
describe('followings actions', () => {
beforeEach(() => {
moxios.install();
store.clearActions();
});
afterEach(() => {
moxios.uninstall();
});
it('dispatches the HANDLE_FAVORITE_SUCCESS action', () => {
moxios.wait(() => {
const request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
payload: {
followingInfoId: '1',
checked: true
}
});
});
const expectedActions = [
{
'type': 'HANDLE_FAVORITE_SUCCESS',
payload: {
followingInfoId: '1',
checked: true
}
}
];
return store.dispatch(actions.handleFavorite()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
这是动作创建者:
export const handleFavorite = data => {
return dispatch => {
return followApi.handleFavorite(data).then(payload => {
dispatch({ type: 'HANDLE_FAVORITE_SUCCESS', payload });
}, err => {
dispatch({ type: 'ERROR', payload: err })
});
}
};
这里是 followApi.handleFavorite:
handleFavorite: (data) => {
return new Promise ((resolve, reject) => {
httpServise.patch(`${host}:${port}/followings/handle-favorite`, data).then(
res => {
if (res.data.payload) {
resolve(res.data.payload);
} else reject({status: 401});
}, err => reject(err)
);
});
},
如果需要,还有 http 服务的一部分:
patch: (url, params) => {
return new Promise((resolve, reject) => {
axios(url, {
method: 'PATCH',
headers: getHeaders(),
data: params
}).then(res => {
resolve(res);
}, err => {
reject(err);
});
});
}
如果你想测试动作创建者,你应该模拟 followApi.handleFavorite
方法而不是 axios
。
这里是测试动作创建者只使用jestjs
和typescript
的解决方案,您可以自己手动模拟模块。
文件夹结构:
.
├── actionCreators.spec.ts
├── actionCreators.ts
├── followApi.ts
└── httpServise.ts
actionCreators.ts
:
import followApi from './followApi';
export const handleFavorite = data => {
return dispatch => {
return followApi.handleFavorite(data).then(
payload => {
dispatch({ type: 'HANDLE_FAVORITE_SUCCESS', payload });
},
err => {
dispatch({ type: 'ERROR', payload: err });
}
);
};
};
followApi.ts
:
import { httpServise } from './httpServise';
const host = 'http://github.com/mrdulin';
const port = 3000;
const followApi = {
handleFavorite: data => {
return new Promise((resolve, reject) => {
httpServise.patch(`${host}:${port}/followings/handle-favorite`, data).then(
(res: any) => {
if (res.data.payload) {
resolve(res.data.payload);
} else {
reject({ status: 401 });
}
},
err => reject(err)
);
});
}
};
export default followApi;
httpService.ts
:
import axios from 'axios';
function getHeaders() {
return {};
}
export const httpServise = {
patch: (url, params) => {
return new Promise((resolve, reject) => {
axios(url, {
method: 'PATCH',
headers: getHeaders(),
data: params
}).then(
res => {
resolve(res);
},
err => {
reject(err);
}
);
});
}
};
actionCreators.spec.ts
:
import configureMockStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import * as actions from './actionCreators';
import followApi from './followApi';
jest.mock('./followApi.ts', () => {
return {
handleFavorite: jest.fn()
};
});
type State = any;
const middlewares = [thunk];
const mockStore = configureMockStore<State, ThunkDispatch<State, undefined, AnyAction>>(middlewares);
const store = mockStore();
describe('followings actions', () => {
beforeEach(() => {
store.clearActions();
jest.resetAllMocks();
});
it('dispatches the HANDLE_FAVORITE_SUCCESS action', () => {
expect.assertions(2);
const mockedHandleFavoritePayload = {
followingInfoId: '1',
checked: true
};
(followApi.handleFavorite as jest.MockedFunction<typeof followApi.handleFavorite>).mockResolvedValueOnce(
mockedHandleFavoritePayload
);
const data = 'jest';
const expectedActions = [
{
type: 'HANDLE_FAVORITE_SUCCESS',
payload: {
followingInfoId: '1',
checked: true
}
}
];
return store.dispatch(actions.handleFavorite(data)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(followApi.handleFavorite).toBeCalledWith(data);
});
});
it('dispatches the ERROR action', () => {
const mockedhHndleFavoriteError = new Error('network error');
(followApi.handleFavorite as jest.MockedFunction<typeof followApi.handleFavorite>).mockRejectedValueOnce(
mockedhHndleFavoriteError
);
const data = 'jest';
const expectedActions = [
{
type: 'ERROR',
payload: mockedhHndleFavoriteError
}
];
return store.dispatch(actions.handleFavorite(data)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(followApi.handleFavorite).toBeCalledWith(data);
});
});
});
具有 100% 覆盖率报告的单元测试结果:
PASS src/Whosebug/52025257/actionCreators.spec.ts (5.95s)
followings actions
✓ dispatches the HANDLE_FAVORITE_SUCCESS action (5ms)
✓ dispatches the ERROR action (2ms)
-------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
actionCreators.ts | 100 | 100 | 100 | 100 | |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 6.87s, estimated 7s
源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/Whosebug/52025257