Created
June 20, 2017 18:39
-
-
Save iansinnott/5a2190269ecf71da45c735dd914a8597 to your computer and use it in GitHub Desktop.
epic testing
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 characters
// 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 characters
// 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 characters
// 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, | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@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.