This post originally appeared on The Quick Left Blog
As with any development, test-driving features is the way to go in a Flux app. As I’ve been learning this technology, I’ve been collecting some of the less obvious patterns that make testing easier. In this post, we’ll take a look at some of these strategies, to make it easier for you to build the next big thing.
Since Flux is a more of an idea than a framework, there’s no convention as to how to structure your project. I personally like to break it down in a fairly obvious fashion, with the different Flux objects grouped together by folder.
1 2 3 4 5 6 7 8
There are a couple of places to put the tests, but I’ve been leaning toward a pattern where the tests live right alongside their corresponding files. This makes it easy to find the test for a given module. It also keeps the directory structures from getting out of sync, as they might if you put everything into a separate
test/ folder. Here’s an example of what this might look like.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
When we find the files to run in our test suite, we can just use globbing to find them all, so it doesn’t really present any problems for setting up our tests.
Facebook recommends using their testing tool, Jest, to test React and Flux components. Although I totally respect Jest, it doesn’t run in the browser, plus I’m pretty used to the toolchain I’m about to describe, so I go about things a slightly different way.
Mocha + Chai
Since there are a lot of dependencies in a Flux app, we’ll probably be doing a lot of stubbing. I like to use SinonJS for this purpose. It gives us stubs and spies, and its API provides the ability to drill down into how functions were called and with what arguments with a level of granularity that can come in really useful.
When it comes to test runners, there are many viable choices. Lately, I’ve been leaning toward Karma for most of my needs, because it’s easy to get set up, and it can be hooked into a coverage tool with ease.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
NPM Build Scripts
As far as running tasks, you can rely on Grunt or Gulp, or you can just set a test script up in your
package.json file. Doing it this way, running tests is as simple as typing
npm test. Here’s what to put into
1 2 3
With these dependencies set up, you’re all ready to start writing tests. Let’s take a look at some of the testing specifics.
Actually Writing Tests
There are four parts to a Flux app:
As mentioned above, Flux is more of a pattern than a framework. Although several people have released experimental frameworks built in its image, there is only one official Facebook package, called
flux. Ironically, it only contains a
Dispatcher. You can find the source code here. Since this package works well and is tested externally, so we won’t be looking at testing
Dispatchers in this post.
Before we get into looking at the remaining parts of Flux in depth, here are a couple of tips that come in handy in all cases.
Setting Up A Sandbox
Sinon sandboxes are a great way to use stubs and spies without having to restore the objects they’re touching later. You can clean things up automatically by setting up a new sandbox before each test and tearing it down afterwards.
1 2 3 4 5 6 7
Sometimes it can be a pain to pull in and/or stub a bunch of dependencies for an object you’re testing. There’s an easy way to grab what you need from within the test: using Rewire, which exposes a special
__get__ method you can use to access whatever you need from the top level scope of the module. You can then stub out methods and properties on those modules. Here’s how to leverage it to your advantage.
1 2 3 4 5 6 7 8 9 10 11
As a note, you don’t even need to use Rewire unless you need access to instance variables on your objects. Since Flux uses plain objects, multiple calls to
require will always return the same object. This means that you can just spy on or stub out a method on one of your
Stores directly after requiring them.
Testing Event Dispatching
When you’re writing a Flux action, it typically sends some kind of event and payload to the
AppDispatcher to trigger events registered elsewhere in the application. It’s easy to spy on the
AppDispatcher and test that it’s called with the right arguments to ensure that your
Action is working properly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
We often load data from a remote server in our
Action objects, so there are typically a lot of promises involved in its internals. When testing these methods, it’s often useful to stub out these promises. It’s pretty easy to do using native promises in ES6. Note that we use
done to ensure that the promise is fully resolved before testing our assertion and moving on to the next test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
In my own Flux projects, I have tried to keep the external API of
stores as “dumb” as possible. They are meant to be simple repositories for business objects that expose an interface for other objects to subscribe to change events. I typically define methods named
removeEventListener for each store.
Despite their relatively simple API, it is vitally important to test your
stores. They’re usually the place where the business logic lives. Plus, they’re responsible for loading data from the server into the client-side app. For these reasons, we want to make sure they work properly. Here are a couple of tricks that can be helpful.
stores are only supposed to accept data through the callback they register with the
dispatcher, it can be tricky to send mocked data into them while testing. Facebook has one suggested way of doing it with Jest, or you can try this approach with Mocha or Jasmine. Alternatively, another nice way to hide the implementation a store uses to fetch its data is to wrap the fetch implementation in an
internals object and test that instead. Here’s what it looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
When it comes to testing this
internals object, we can test that the
internals methods are behaving as expected. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Using Dependency Injection
If you write your
stores in an object-oriented way, you can pass a reference to the
dispatcher directly into them. This makes it easier to test that different dispatching events trigger the correct callbacks to produce the behavior that is desired. Here’s an example (thanks to Jack Hsu for the inspiration for this tip).
1 2 3 4 5 6 7 8 9 10 11 12
And now testing the store is simple. We can just inject a dispatcher and use it to trigger the events we want to test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Finally we come to the view layer. If you’ve played with React, writing these view components should come naturally.
When it comes to testing, there are a lot of things you can safely skip, since testing them would just be verifying that React works as expected. For example, checking that
onClick handlers fire is pointless, since we know that React will call them. On the other hand, it can be useful that the behavior we want them to cause is actually carried out.
Wrap Components With StubRouterContext
It’s usually a good idea to wrap your components in a stubbed out context, to make it easier to force them to behave the way you want within your tests. If you don’t, it can be hard to get them to render and behave as expected.
To get this to happen, I recommend using the stub-router-context module from the react-router project. It’s useful for wrapping the context of all kinds of components aside from the React Router. Although I tend to stick to the name “Stub Router Context”, it would perhaps be more accurate to just call it “stub context”, since you can use it to stub out any context.
I also like to add a ref to the stub in the component that’s returned in
render, to make it easier to get hold of the component being wrapped by the component returned by
1 2 3
Here’s how it looks when you include it in your test. Note how the ref I included makes it easy to grab the child and call
setState on it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Use the React TestUtils
When it comes to rendering your component into a test DOM, checking whether classes are being dynamically added or removed, or whether input values are are changing in response to user interactions, the React TestUtils can’t be beat. Get to know the TestUtils API, and use it to test your view components. It makes things much less painful.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
In this post, we explored how to better test Flux applications. We took a look at some of the JS testing tools that can be helpful to get setup in your build process. We talked about some testing tips that are useful across all of the different Flux objects. Then we drilled down into testing tips specific to
I hope that some of these tips come in useful for your team as you build your cutting-edge web application. Best of luck!