Writing Epic Unit Tests

Kevin Salter
Nov 20, 2016 · 3 min read

unit testing epics when using redux-observable

Image for post
Image for post

In my last post I talked about what redux-observable middleware is, and how you might apply it to solve a practical problem (autosave).

In this follow-up post, I’ll show you how easy and fun it is to do some epic unit testing! (this, and all further puns totally intended) As a quick reminder, this is roughly what the UI looked like in the last post, while the saveFieldEpic.js epic was listening for actions dispatched from this input component’s onChange event:

Image for post
Image for post
autosaving a field as the user types

Before we starting writing the code for our tests, we can make testing easier by injecting dependencies into all epics (that we’ll want to replace later in our tests) when we combine epics, right before calling createEpicMiddleware (full credit to Jay Phelps for this info!).

const rootEpic = (...args) => combineEpics(
saveFieldEpic,
)(...args, {ajax});

In this example, when we’re creating the Redux store, we inject ajax from rxjs/observable/dom/ajax which will now appear as the third argument in our epics (right after action$ and store). Again, this is so we can replace it with another function during testing that will not actually send an XHR over the network.

The code in our saveFieldEpic.js file from the last post will now look more like this, where we pass action$ on line 14 as usual, then null for the store value (since we won’t need it), and finally {ajax}.

*note: using null for the store value might lead to build errors, in which case you can just use store (updated Nov. 23, 2017)

Now that we’ve injected the AJAX dependency, and our epic is already operating in isolation, we can go about testing the inputs and outputs of the entire epic function itself (without having to mock the store).

By importing ActionsObservable from the redux-observable library, we can dispatch mock actions into the epic under test. Next we can create our mock version of the AJAX function, which will either return Observable.of({}) (or whatever kind of server response you’d like) if the AJAX call should be successful, or Observable.throw('something bad!') if the AJAX call should fail.

Finally, we import the epic, feed it that input, and call toArray on it so that when we subscribe to the epic’s output it will return an array of every action that epic produced as output! Asserting that actualOutputActions deeply equals expectedOutputActions gives us our passing tests!

Here’s how that would look in code:

I think the simplicity of this is a beautiful thing! I’ve also been finding that this technique remains super powerful when used in even more complicated scenarios. And, not least of all, these tests are a ton of fun to write. 🚀

Kevin Salter’s Blog

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store