Krzysztof Żuraw Blog

Dependency injection in redux-observable

June 15, 2018

Imagine that you have following situation: your calls to external api are using helper modules where all request logic lies. For instance, you have backendService:

export const makeRequest = async () => return fetch.get('http://www.some_api.com');

In your epic you use this as follows:

import * as backendService from './backendService'

const fetchBackendServiceEpic = (action$, store) =>
  action$
    .filter(isActionOf(YOUR_TRIGGER_ACTION))
    .switchMap(action => Observable.from(backendService.makeRequest()))
    .mapTo(YOUR_SUCCESS_ACTION)
    .catch(err => Observable.of(YOUR_ERROR_ACTION))

Everything seems fine but to test this epic you have to mock whole API request to http://www.some_api.com using for instance nock . Your test starts to get a little bit heavy to write:

describe('fetchBackendServiceEpic', () => {
  it.should('fetch service', () => {
    const mockStore = configureMockStore()
    nock(`http://www.some_api.com`)
      .get()
      .reply(200, {})
    return rootEpic(ActionsObservable.from(YOUR_TRIGGER_ACTION), mockStore)
      .toArray()
      .toPromise()
      .then(actions => {
        expect(actions).toEqual([YOUR_TRIGGER_ACTION, YOUR_SUCCESS_ACTION])
      })
  })
})

Can you do better? Yes, using dependency injection. Your epic is depended on backendService. How can you inject it to your epic? The actual way of doing this is in the documentation : Injecting Dependencies Into Epics · redux-observable. Thanks to that you have your epic with dependency injected:

const fetchBackendServiceEpic = (action$, store, { makeRequest }) =>
  action$
    .filter(isActionOf(YOUR_TRIGGER_ACTION))
    .switchMap(action => Observable.from(backendService.makeRequest()))
    .mapTo(YOUR_SUCCESS_ACTION)
    .catch(err => Observable.of(YOUR_ERROR_ACTION))

To test it you need:

describe('fetchBackendServiceEpic', () => {
  it.should('fetch service', () => {
    const dependencies = { makeRequest: jest.fn().mockReturnValue([{}]) }

    fetchBackendServiceEpic(YOUR_TRIGGER_ACTION, (store = null), dependencies)
      .toArray()
      .subscribe(actions => {
        expect(actions).toEqual([YOUR_TRIGGER_ACTIONS, YOUR_SUCCESS_ACTION])
      })
  })
})

As you see here there is just injecting dependency with your desired return value - you are testing just logic of your epics and if proper actions are dispatched of your TRIGGER_ACTION.

Summary

As you can see dependency injection in epics can be a powerful way to ease your testing of epics.

Tagged with rxjs redux react javascript redux-observable
Want a monthly digest of these blog posts?

I turned off Disqus comments. If you want to give me feedback please write to krzysztofzuraw(at)fastmail.com or use Keybase.


Krzysztof ŻurawDelivered by Krzysztof Żuraw. Opinions are my own. You can follow updates via RSS feed.