[Redux-Observable && Unit Testing] Use tests to verify updates to the Redux store (rxjs scheduler)

本文介绍了一种使用RxJS调度器来测试Redux中异步操作的方法。通过配置虚拟时间调度器,可以确保异步操作按预期执行并更新状态。文章详细展示了如何设置测试环境,并提供了一个具体的例子。

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

转载于:https://www.cnblogs.com/Answer1215/p/7683480.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值