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

  • 相关阅读:
    交换机的配置文件和系统映像文件备份与恢复(如果不小心损坏了,可以用这种方法恢复)
    交换机端口安全配置
    路由器密码重置(不是适用于所有有些启动顺序数字不一样)
    交换机的默认网关(跨网段telnet)
    思科交换机的初始配置(使用telnet登录)
    Linux命令集(第一部分共40个)
    Linux6.9安装
    javamail邮件发送报错解决方案
    isEmpty()与equals()、==“”区别
    eclipse svn 修改了类名之后提交
  • 原文地址:https://www.cnblogs.com/Answer1215/p/7683341.html
Copyright © 2011-2022 走看看