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

  • 相关阅读:
    server version for the right syntax to use near 'USING BTREE 数据库文件版本不合导致的错误
    百度网盘,FTP上传异常、上传失败的解决办法
    zencart产品属性dropmenu select只有一个选择项时自动变成radio单选的解决办法
    火车采集小结
    dedecms织梦移站后替换数据库中文件路径命令
    dedecms织梦网站本地迁移到服务器后,后台更新栏目文档提示模板文件不存在,无法解析文档!的解决办法
    Addthis分享插件后url乱码的解决办法
    dedecms织梦做中英文(多语言)网站步骤详解
    递归的参数和返回值
    【图论算法】Dijkstra&BFS
  • 原文地址:https://www.cnblogs.com/Answer1215/p/7683341.html
Copyright © 2011-2022 走看看