Created
June 20, 2017 18:39
-
-
Save iansinnott/5a2190269ecf71da45c735dd914a8597 to your computer and use it in GitHub Desktop.
Revisions
-
iansinnott created this gist
Jun 20, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,6 @@ // 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 }); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,18 @@ // 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; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,63 @@ // 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, }); });