This repo is a simple example project demonstrating how to test the different components of a Redux application.
The general idea: Don't write individual unit tests for your action creators, reducers, and selectors; instead, write tests that test the whole "duck".
This strategy comes from this Redux issue thread in which @bvaughn describes a way of testing Redux applications in order to get "the most bang for your buck" (dare I say "bang for your duck"?).
See the motivation section for more details on this testing strategy.
Examples of bad duck tests are found here.
Examples of good duck tests are found here.
Examples of good duck tests involving sagas are found here.
First of all, what is a duck? It's a combination of related action creators, a reducer, and selectors. (It can also include other Redux-related paraphernalia, such as sagas.) Action creators create actions that when dispatched, cause state changes in the reducer, which ultimately are manifested in different values returned by the selectors.
You can read more about ducks in the ducks-modular-redux project.
How do we test our ducks? The redux documentation gives examples of how to write unit tests for action creators, reducers, and selectors, i.e. how to test them each in isolation. So why don't we start by writing unit tests? That's what the testing pyramid suggests, after all:
There are some examples of these kind of unit tests in this project, in one of the duck test files.
Let's step back for a moment and recall the purpose of tests:
- Tests give us confidence that our code is working as intended
- Tests give us confidence that our code is working as intended after we do refactoring
So good tests have the following properties:
- They fail when the code doesn't work anymore
- They don't fail when the code is still working
- They test code as it actually is used in production
With that in mind, consider what happens to our isolated action creator tests, reducer test, and selector tests when we do the following:
- Refactor our state shape
- Accidentally introduce a bug
It turns out lots of bad things happen:
- The reducer tests fail upon refactoring, even though our code is still working, because they test implementation details. We have to mock the state.
- The selector tests disconcertingly DON'T fail when we introduce a bug in the state, because we're mocking the state. We don't have confidence that the entire action -> reducer -> selector contract is fulfilled.
- Not all of the tests pass even when the code is actually working.
How do we fix these problems?
Write tests that test the whole duck! You can see examples of such tests in another duck test file. Notice how these tests are:
- Resilient to state refactoring
- Test the real state shape using a real store, just like what happens in production
- Still fast
- Give confidence that all of our action -> reducer -> selector contracts are fulfilled, because it actually tests the contract
Think of a duck as having a public API: action creators and selectors are how you interact with the duck. They represent a "public interface" to the duck. If you only write tests using action creators and selectors, your tests are resilient to change as long as you maintain the same interface for your action creators and selectors.
You're not going to have an action if it doesn't trigger a state change, or at least have the potential to trigger some state change. You're not going to have a selector unless it gives you part of the state that gets changed by the dispatch of some action. So when it comes to ducks, don't think of isolated unit tests, think of the combined action/reducer/selector tests. The unit you want to be testing is the "three-part unit" of action creator, reducer, and selector. They are not distinct entities, they only have meaning when considered in relation to each other.
Set up:
yarn install
yarn start
Run tests:
yarn test
Thanks goes to these wonderful people (emoji key):
Michael Rose 💻 |
Christian Battista 📖 |
Noah 📖 |
This project follows the all-contributors specification. Contributions of any kind welcome!
Thanks to LogoMakr for having an impressive bounty of free stock duck images.