Skip to content

Instantly share code, notes, and snippets.

@iansinnott
Created June 20, 2017 18:39
Show Gist options
  • Save iansinnott/5a2190269ecf71da45c735dd914a8597 to your computer and use it in GitHub Desktop.
Save iansinnott/5a2190269ecf71da45c735dd914a8597 to your computer and use it in GitHub Desktop.
epic testing
// actions.js
export const FETCH = 'someModule/FETCH';
export const FETCH_SUCCESS = 'someModule/FETCH_SUCCESS';
export const FETCH_FAILURE = 'someModule/FETCH_FAILURE';
export const fetch = () => ({ type: FETCH });
// epics.js
import { FETCH } from './actions.js';
const epic = (action$, store, { ajax }) =>
action$.ofType(FETCH)
.switchMap(() =>
ajax.get('/api/things')
.map(({ request }) => ({
type: FETCH_SUCCESS,
payload: request,
}))
.catch(err => Observable.of({
type: FETCH_FAILURE,
payload: err,
error: true,
})));
export default epic;
// test.js
import test from 'ava';
import { ActionsObservable } from 'redux-observable';
import { Observable } from 'rxjs';
import { combineReducers } from 'redux-immutable'; // For anyone not using Immutable.js this would just come from redux
import { createStore } from 'redux';
import sinon from 'sinon';
import { fetch, FETCH_SUCCESS, FETCH_FAILURE } from './actions.js';
import reducer from './index.js'; // The reducer file isn't included in this gist example
import epic from './epics.js';
const ENDPOINT = '/api/things';
const testError = new Error('@@TEST_ERROR');
// Simulate an object with a similar shape to the result of Observable.ajax
const mockRequest = (body, rest = {}) => ({
response: body,
responseType: 'json',
status: 200,
...rest,
});
// Your store needs depend on the epics. In this example no store is needed at
// all, but I'm putting this in here for my own reference
const createTestStore = () => createStore(combineReducers({ someModule: reducer }));
// Mock Observable.ajax.get. In the running app all the epics would be passed
// a more full API but we only need to mock what we test. The returned request
// body is arbitrary. You could mock it to look like real successful api calls
const createAjax = () => ({
get: sinon.stub().returns(Observable.of(request('@@TEST_SUCCESS'))),
});
// Simulate request failure. I don't stub this one out since there would be no
// need to test it unless we cared about its args. Stub it if you need to.
const createAjaxError = () => ({
get: () => Observable.throw(testError),
});
test('fetch epic', async t => {
const ajax = createAjax();
const store = createTestStore();
const action$ = ActionsObservable.of(fetch());
let result;
// Test success
result = await epic(action$, store, { ajax }).toPromise();
t.true(ajax.get.calledWith(ENDPOINT));
t.deepEqual(result, {
type: FETCH_SUCCESS,
payload: '@@TEST_SUCCESS',
});
// Test failure
result = await epic(action$, store, { ajax: createAjaxError() }).toPromise();
t.deepEqual(result, {
type: FETCH_SUCCESS,
payload: testError,
error: true,
});
});
@iansinnott
Copy link
Author

@BerkeleyTrue ah, good question. I found the ava support for Rx observables to be a bit lacking so I went with this approach. Maybe I was using it wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment