Reactjs 可观察到的重复次数:epic的jest测试失败

Reactjs 可观察到的重复次数:epic的jest测试失败,reactjs,unit-testing,jestjs,redux-observable,Reactjs,Unit Testing,Jestjs,Redux Observable,我遵循从到测试epic的步骤 ... store.dispatch({ type: FETCH_USER }); expect(store.getActions()).toEqual([ { type: FETCH_USER }, { type: FETCH_USER_FULFILLED, payload } ]); ... 但我失败了,因为第二个动作在稍后收到,如下所示 Test failed Expected value to equal: [{"type

我遵循从到测试epic的步骤

...
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))
    })

原因是承诺总是在上解析,因此您的
api.fetchUser()
不会同步发出

您需要模拟它,使用类似于
Promise.resolve()。然后(()=>expect(store.getActions)。toEqual(…)
的方法等待下一个微任务,或者您可以直接测试您的epics而不使用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的新依赖注入功能:




首先,使用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)
      )));
const fetchDataEpic:Epic=action$=>
行动$
.类型(FETCH_型号)
.mergeMap((操作:FetchModel)=>
FetchDynamicATA(action.url、action.params)
.map((负载:FetchedData)=>fetchModelSucc(负载.data))
.catch(错误=>Observable.of(
fetchModelFail(错误)
)));
然后,您可能需要一段时间来决定何时完成测试

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
      }
    ]);
  });
});
描述(“史诗”,()=>{ 让商店:MockStore; 在每个之前(()=>{ store=mockStore(); }); 之后(()=>{ nock.cleanAll(); epic.replacepic(epic); }); 它(“获取数据模型成功”,()=>{ 常数有效载荷={ 代码:0, 数据:一些数据, 标题:{}, 味精:“好的” }; 常量参数={ 数据1:100,, 数据2:“4” }; 常量mock=nock(“https://test.com") .get(“/test”) .query(参数) .答复(200,有效载荷); const go=新承诺((解决)=>{ 仓库调度({ 类型:FETCH_模型, url:“https://test.com/test", params }); 让间隔:数字; 间隔=窗口。设置间隔(()=>{ if(mock.isDone()){ 间隔时间; 解析(store.getActions()); } }, 20); }); 返回expect(go).resolves.toEqual([ { 类型:FETCH_模型, url:“https://test.com/assignment", params }, { 类型:获取\模型\成功, 数据:somData } ]); }); });
享受它:)

我发现解决方案是正确的。我得到了“TypeError:Cannotreadproperty'\u location'of null”,尽管这不在问题的范围之内。我在这上面花了几个小时。。。尝试此操作后出现“…toArray不是函数”错误。是的,RxJS用户始终可以决定如何导入运算符。仍然存在问题。。。带有Observable.ajax的epic返回DomeException InvalidAccessError。我在开玩笑。似乎它没有正确地模仿…问题不在ajax调用中。任何可观察到的异步执行都会导致超时。对
done
的调用从未发生过。
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
      }
    ]);
  });
});