zoukankan      html  css  js  c++  java
  • [Redux-Observable && Unit Testing] Mocking an ajax request when testing epics

    Often in unit tests we are focussing on the logic involved in crafting a network request, & how we respond to the result. The external service is unlikely to be under our control, so we need a way to ‘mock’ the Ajax request in a way that allows us to focus on the logic. In this lesson we’ll see how we can pass in dependencies into epics to make testing things Ajax requests easier.

    In a real world React app, for one epic, we might have some dependecies. For example, ajax call. To make it easy for testing, we can make those deps as injectable deps.

    When creating root epic:

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

    Using it in Epic:

    // Notice the third argument is our injected dependencies!
    const fetchUserEpic = (action$, store, { getJSON }) =>
      action$.ofType('FETCH_USER')
        .mergeMap(({ payload }) =>
          getJSON(`/api/users/${payload}`)
            .map(response => ({
              type: 'FETCH_USER_FULFILLED',
              payload: response
            }))
        );

    ---------------Test example ---------------------

    index.js, root setup

    import {createStore, applyMiddleware, compose} from 'redux';
    import {Provider} from 'react-redux';
    import reducer from './reducers';
    import { ajax } from 'rxjs/observable/dom/ajax';
    
    import {createEpicMiddleware} from 'redux-observable';
    import {rootEpic} from "./epics/index";
    
    const epicMiddleware = createEpicMiddleware(rootEpic, {
      dependencies: {
        ajax
      }
    });
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    
    const store = createStore(
      reducer,
      composeEnhancers(
        applyMiddleware(epicMiddleware)
      )
    );

    Epic function:

    import {Observable} from 'rxjs';
    import {combineEpics} from 'redux-observable';
    import {CANCEL_SEARCH, receiveBeers, searchBeersError, searchBeersLoading, SEARCHED_BEERS} from "../actions/index";
    
    const beers  = `https://api.punkapi.com/v2/beers`;
    const search = (term) => `${beers}?beer_name=${encodeURIComponent(term)}`;
    
    export function searchBeersEpic(action$, store, deps) {
      return action$.ofType(SEARCHED_BEERS)
        .debounceTime(500, deps.scheduler)
        .filter(action => action.payload !== '')
        .switchMap(({payload}) => {
    
          // loading state in UI
          const loading = Observable.of(searchBeersLoading(true));
    
          // external API call
          const request = deps.ajax.getJSON(search(payload))
            .takeUntil(action$.ofType(CANCEL_SEARCH))
            .map(receiveBeers)
            .catch(err => {
              return Observable.of(searchBeersError(err));
            });
    
          return Observable.concat(
            loading,
            request,
          );
        })
    }
    
    export const rootEpic = combineEpics(searchBeersEpic);

    Test code:

    import {Observable} from 'rxjs';
    import {ActionsObservable} from 'redux-observable';
    import {searchBeersEpic} from "./index";
    import {RECEIVED_BEERS, searchBeers, SEARCHED_BEERS_LOADING} from "../actions/index";
    
    it.only('should perform a search', function () {
      const action$ = ActionsObservable.of(searchBeers('shane'));
    
      const deps = {
        ajax: {
          getJSON: () => Observable.of([{name: 'shane'}])
        }
      };
    
      const output$ = searchBeersEpic(action$, null, deps);
    
      output$.toArray().subscribe(actions => {
        expect(actions.length).toBe(2);
    
        expect(actions[0].type).toBe(SEARCHED_BEERS_LOADING);
        expect(actions[1].type).toBe(RECEIVED_BEERS);
      });
    });

    Refs: Link

  • 相关阅读:
    cancel_delayed_work和flush_scheduled_work【转】
    linux中断申请之request_threaded_irq【转】
    VELT-0.1.5开发:使用kgdb调试Linux内核【转】
    iOS_第3方类库_BlurAlertView_GPUImage
    一个轻client,多语言支持,去中心化,自己主动负载,可扩展的实时数据写服务的实现方案讨论
    如果数据文件顺序被打乱,你有办法找回正确的文件把数据库打开吗?
    Hibernate级联操作和载入机制(二) cascade and fetch
    UVA 10557 XYZZY
    Arcgis for javascript map操作addLayer具体解释
    64位CentOS上编译 Hadoop 2.2.0
  • 原文地址:https://www.cnblogs.com/Answer1215/p/7683341.html
Copyright © 2011-2022 走看看