Injecting Dependencies Into Epics

将依赖注入到你 Epics 中帮助测试。

假设你需要和网络交互。你可能会直接从 rxjs 中使用 ajax 工具:

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchUserEpic = (action$, store) =>
  action$.ofType('FETCH_USER')
    .mergeMap(({ payload }) =>
      ajax.getJSON(`/api/users/${payload}`)
        .map(response => ({
          type: 'FETCH_USER_FULFILLED',
          payload: response
        }))
    );

但是这种方式有一个问题: 包含 epic 的文件直接导入了它的依赖,所以 mocking 就会非常困难。

一种方式可能是 mock window.XMLHttpRequest, 但是这工作量很大并且你不是在仅仅测试你的 Epic,你是在测试 RxJS 正确使用 XMLHttpRequest,事实上,这并不是你的测试目的。

注入依赖

你可以使用 createEpicMiddlewaredependencies 配置项来注入依赖:

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

任何你提供的都会在创建 store 之后被当作所有 Epics 的第三个参数传入。

现在你的 Epic 可以使用注入的 getJSON,而不用导入:

// 注意第三个参数是我们注入的依赖!
const fetchUserEpic = (action$, store, { getJSON }) =>
  action$.ofType('FETCH_USER')
    .mergeMap(({ payload }) =>
      getJSON(`/api/users/${payload}`)
        .map(response => ({
          type: 'FETCH_USER_FULFILLED',
          payload: response
        }))
    );

为了测试,你可以直接调用 Epic,传递 mock 的 getJSON:

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; // 该 epic 不需要
const dependencies = {
  getJSON: url => Observable.of(mockResponse)
};

// 将这个例子适用到你的测试框架和特定用例 
fetchUserEpic(action$, store, dependencies)
  .toArray() // 缓冲所有发出的 actions 直到 epic 自然完成
  .subscribe(actions => {
    assertDeepEqual(actions, [{
      type: 'FETCH_USER_FULFILLED',
      payload: mockResponse
    }]);
  });

results matching ""

    No results matching ""