zoukankan      html  css  js  c++  java
  • [Redux-Observable && Unit Testing] Use tests to verify updates to the Redux store (rxjs scheduler)

    In certain situations, you care more about the final state of the redux store than you do about the particular stream of events coming out of an epic. In this lesson we explore a technique for dispatching actions direction into the store, having the epic execute as they would normally in production, and then assert on the updated store’s state.

    To test a reducer, what we need to do is actually dispatch as action with its payload.

    store.dispatch(action);

    But before that, we need to get our 'store' configuration in the test.

    configureStore.js:

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

    index.js:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import {Provider} from 'react-redux';
    import {configureStore} from "./configureStore";
    
    const store = configureStore();
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>
      , document.getElementById('root'));

    The configureStore.js exports function which create a store, we can import normally in the test file.

    Now for example, we want to dispatch this action:

    export function searchBeers(query) {
      return {
        type: SEARCHED_BEERS,
        payload: query
      }
    }

    Epic:

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

    'decountTime' make the Epic async!

    To verifiy the result is correct, we can do

      const store = configureStore(deps);
    
      const action = searchBeers('name');
    
      store.dispatch(action);
    
      expect(store.getState().beers.length).toBe(1);

    BUT, actually this test code won't work, because the 'decountTime' in the epic, makes it as async opreation. Reducer expects everything happens sync...

    One way can test it by using 'scheduler' from rxjs.

    import {Observable} from 'rxjs';
    import {VirtualTimeScheduler} from 'rxjs/scheduler/VirtualTimeScheduler';
    import {searchBeers} from "../actions/index";
    import {configureStore} from "../configureStore";
    
    it('should perform a search (redux)', function () {
    
      const scheduler = new VirtualTimeScheduler();
      const deps = {
        scheduler,
        ajax: {
          getJSON: () => Observable.of([{name: 'shane'}])
        }
      };
    
      const store = configureStore(deps);
    
      const action = searchBeers('shane');
    
      store.dispatch(action);
    
      scheduler.flush();
    
      expect(store.getState().beers.length).toBe(1);
    });

    And we need to modifiy the epic:

    .debounceTime(500, deps.scheduler)

    Take away, we can test async oprations by using 'scheduler' from rxjs. 

    -------------------FUll Code------------

    Github

  • 相关阅读:
    mysql 查看某数据库各个表容量大小SQL
    Gated RNN(《深度学习进阶》第六章总结)
    RNN(《深度学习进阶》第五章总结)
    word2vec的改进(《深度学习进阶》第四章总结)
    201521123024 《Java程序设计》 第九周学习总结
    如何在vue项目中使用md5及base64加密
    vite+vue3.0+vue-router+vuex快速搭建项目
    vite+vue3.0搭建项目
    MySQL创建计划任务
    MySQL基础函数
  • 原文地址:https://www.cnblogs.com/Answer1215/p/7683480.html
Copyright © 2011-2022 走看看