我按照documentation的步骤来测试史诗。
...
store.dispatch({ type: FETCH_USER });
expect(store.getActions()).toEqual([
{ type: FETCH_USER },
{ type: FETCH_USER_FULFILLED, payload }
]);
...
但是我失败了,因为稍后收到了第二个动作,就像跟随。
Test failed
Expected value to equal:
[{"type": "FETCH_USER"}, {"type": "FETCH_USER_FULFILLED", "payload": [some]}]
Received:
[{"type": "FETCH_USER"}]
Difference:
- Expected
+ Received
@@ -1,20 +1,5 @@
Array [
Object {"type": "FETCH_USER"},
Object {"type": "FETCH_USER_FULFILLED", "payload": [some]} ] // this is what should be.
所以我想我应该知道派遣何时完成或类似的事情。 我该如何解决这个问题?
我使用了fetch()和Rx.Observable.fromPromise而不是ajax.getJSON()
这是我的史诗。
const fetchUserEpic = (action$) =>
action$
.ofType(FETCH_USER)
.mergeMap(() => {
return Rx.Observable.fromPromise(api.fetchUser())
.map((users) => ({
type: FETCH_USER_FULFILLED,
payload: { users }
}))
.catch((error) => Rx.Observable.of({
type: FETCH_USER_ERROR,
payload: { error }
}))
.takeUntil(action$.ofType(FETCH_USER_CANCELLED))
})
答案 0 :(得分:5)
原因是承诺始终在next microtask上解决,因此您的api.fetchUser()
不会同步发出。
你需要嘲笑它,使用Promise.resolve().then(() => expect(store.getActions).toEqual(...)
之类的东西等到下一个微任务,或者你可以尝试直接测试你的史诗 而不使用redux。
it('Epics with the appropriate input and output of actions', (done) => {
const action$ = ActionsObservable.of({ type: 'SOMETHING' });
somethingEpic(action$, store)
.toArray() // collects everything in an array until our epic completes
.subscribe(actions => {
expect(actions).to.deep.equal([
{ type: 'SOMETHING_FULFILLED' }// whatever actions
]);
done();
});
});
当我(或其他人)有时间编写文档时,这将是我们在文档中首选的测试故事。因此,我们不是在测试中使用redux和中间件,而是直接使用我们自己的模拟调用epic函数。更简单,更清洁。
通过这种方法,我们可以利用redux-observable的新依赖注入功能:https://redux-observable.js.org/docs/recipes/InjectingDependenciesIntoEpics.html
import { createEpicMiddleware, combineEpics } from 'redux-observable';
import { ajax } from 'rxjs/observable/dom/ajax';
import rootEpic from './somewhere';
const epicMiddleware = createEpicMiddleware(rootEpic, {
dependencies: { getJSON: ajax.getJSON }
});
// Notice the third argument is our injected dependencies!
const fetchUserEpic = (action$, store, { getJSON }) =>
action$.ofType('FETCH_USER')
.mergeMap(() =>
getJSON(`/api/users/${payload}`)
.map(response => ({
type: 'FETCH_USER_FULFILLED',
payload: response
}))
);
import { ActionsObservable } from 'redux-observable';
import { fetchUserEpic } from './somewhere/fetchUserEpic';
const mockResponse = { name: 'Bilbo Baggins' };
const action$ = ActionsObservable.of({ type: 'FETCH_USERS_REQUESTED' });
const store = null; // not needed for this epic
const dependencies = {
getJSON: url => Observable.of(mockResponse)
};
// Adapt this example to your test framework and specific use cases
fetchUserEpic(action$, store, dependencies)
.toArray() // buffers all emitted actions until your Epic naturally completes()
.subscribe(actions => {
assertDeepEqual(actions, [{
type: 'FETCH_USER_FULFILLED',
payload: mockResponse
}]);
});
答案 1 :(得分:0)
首先,使用isomorphic-fetch而不是Observable.ajax来支持nock,就像这样
const fetchSomeData = (api: string, params: FetchDataParams) => {
const request = fetch(`${api}?${stringify(params)}`)
.then(res => res.json());
return Observable.from(request);
};
所以我的史诗是:
const fetchDataEpic: Epic<GateAction, ImGateState> = action$ =>
action$
.ofType(FETCH_MODEL)
.mergeMap((action: FetchModel) =>
fetchDynamicData(action.url, action.params)
.map((payload: FetchedData) => fetchModelSucc(payload.data))
.catch(error => Observable.of(
fetchModelFail(error)
)));
然后,您可能需要一个间隔来决定何时完成测试。
describe("epics", () => {
let store: MockStore<{}>;
beforeEach(() => {
store = mockStore();
});
afterEach(() => {
nock.cleanAll();
epicMiddleware.replaceEpic(epic);
});
it("fetch data model succ", () => {
const payload = {
code: 0,
data: someData,
header: {},
msg: "ok"
};
const params = {
data1: 100,
data2: "4"
};
const mock = nock("https://test.com")
.get("/test")
.query(params)
.reply(200, payload);
const go = new Promise((resolve) => {
store.dispatch({
type: FETCH_MODEL,
url: "https://test.com/test",
params
});
let interval: number;
interval = window.setInterval(() => {
if (mock.isDone()) {
clearInterval(interval);
resolve(store.getActions());
}
}, 20);
});
return expect(go).resolves.toEqual([
{
type: FETCH_MODEL,
url: "https://test.com/assignment",
params
},
{
type: FETCH_MODEL_SUCC,
data: somData
}
]);
});
});
享受它:)