Writing Tests in Manifold

This is an advanced topic. Please make sure you understand React/Redux and redux-saga before proceeding.

Jest

Manifold is set up with a test suite called Jest. Here is the Jest website: https://facebook.github.io/jest/. You will notice that Facebook has also written and currently maintains Jest. To avoid repetition, check out this part of the Manifold README.md: https://github.com/Picolab/Manifold/blob/master/README.md#running-tests.

To quickly sum up Jest, it is a command line tool that we can use to run all or a part of our written tests.

Getting Jest to Run

After running the command:

npm test

You may hit an error describing a watcher failing. To fix this (if you are using a mac), run the following brew command (if you don't have brew, go ahead and install it here: https://brew.sh/):

brew install watchman

Watchman itself is not a dependency in the project, and as such is not included in the dependency tree. Brew will install watchman globally on your computer. Running the above npm command should now open the Jest test suite.

Writing New Tests

There are a two locations to write tests. Under the src folder, there is a "__tests__" folder that Jest will automatically locate. Also, any files with a test.js or spec.js extension will be recognized. 

Pushing Code

Avoid pushing code that fails tests. By default, Jest will only run test cases related to code that has been changed since the latest commit. Of course, you can explicitly run all the test cases if you'd like, but you'd have to remember to do that. If you push code that fails a test case, then when others pull that code, Jest won't run the test cases by default that would catch those problems.

Types of Tests

Certainly, there are many different types of tests you could write/perform, from deep tests to shallow ones. This is not a guide attempting to explain all the different tests you could ever perform, rather some of the different kinds of tests we are currently performing in Manifold. This will help you identify what each test is trying to accomplish in addition to any other comments in the test file itself. We'll try to keep this page updated as we use new or different forms of test.

Smoke Tests

A smoke test is a very simple, sanity check form of test case. All they essentially do is make sure a component renders without crashing. It requires very little effort and can help find crashes in places you very well might miss. It has already helped find a few bugs and outdated dependencies even during the writing of this page. Here is the example code from the Create React App README.md:

Smoke Test
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
});

Snapshot Tests

Snapshots are a bit confusing, but you can read about them here: https://facebook.github.io/jest/docs/en/snapshot-testing.html. Snapshots are generally used to protect components from physically changing. It takes a snapshot of what the component should look like when rendered, and when running tests in the future, will take another snapshot and compare it with the previous snapshot stored from the initial run. If something physical (like the class of an html element) changed, then the test will fail. This failure simply means it doesn't look like how it used to. If you intentionally made physical changes to the component, then you can update the snapshot. Otherwise you'd need to fix the code.

Why snapshots?

From Ben McCormick's blog: https://benmccormick.org/2016/09/19/testing-with-jest-snapshots-first-impressions/,

"UI components often change in small and trivial ways. Copy is changed, whitespace is added, a border color is modified. Generally this means that developers have to choose between detailed tests that catch any regression but require constant updates, or less detailed tests that focus on core behaviors but miss smaller regressions.

Snapshot tests provide a new way of approaching these problems in unit tests. "

Testing Redux Components

It would be helpful to review the CreateThingModal.js and CreateThingModal.test.js files, as they are good examples of different parts of a component to test and how to test them. It contains more comments than the average file and will help beginners catch on to the Enzyme/Jest environment.

Testing a Redux component can be very tricky. First off, and as a reminder, a Redux component is a component connected to the Redux store using the connect() function. If we were to simply try rendering a shallow component with Enzyme, like:

Shallow Rendering
const wrapper = shallow(<ReduxComponent />)

We would quickly get an error from jest complaining about how we are missing a store, either in the Provider or context. This brings in the idea of mocking. In this case, we have a component that relies on the data store, but we want to declare different stores for different kinds of tests, because different tests might rely on different data in the store. Mocking is setting up the dependencies (in this case the Redux store) to fit our test case. We use a mock store package called redux-mock-store in order to easily set up a mock store with whatever variables we want. You can look at the npm package, or the examples provided in any of our own components that have tests, to find out exactly what dependencies to import and how to set up the mock store. 

But how do we actually connect the store to the Enzyme shallow function above? The shallow() function takes an optional second parameter, which is an object. The object will contain a key called context, whose value is yet another object. In that second object we can put our mock store under the key "store" like this:

Mock store with Enzyme
const store = mockStore({});
const wrapper = shallow(<ReduxComponent />, { context: { store: store } });

Now our mock store is all set up! If we want the store to actually have variables now, we can include it in the first line of code above as the object in the mockStore() function. For a more complete example, look at the code in some of our test cases.

Smoke Tests with Redux Components

If the only thing you are interested in is a smoke test to make sure the component renders, you can shallow render the component that is not connected to the store. To do this, you need to export the component class from the file besides the normal default export of the connected component. That looks like this:

export class MyComponent extends Component{ /* */ }//this export is the one we want

export default connect()(MyComponent)

Now when we import it for rendering, we need to grab a specific export rather than the default export like this:

//import MyComponent from './MyComponent' //this grabs the default component.
import { MyComponent } from './MyComponent' //this grabs a specific export with the name MyComponent

Then the shallow function will work without needing to mock the redux store.

const wrapper = shallow(<MyComponent />)//this will work now


Testing Redux Actions

Testing Redux Reducers

Testing redux-saga sagas

Redux Saga can be quite tricky to test, since it is essentially built on async actions and also because it inherently relies on javascript generators. We can approach testing a saga by looking at two different aspects; we can look at the dispatched actions that should result under certain conditions, or we could test the generator function's steps and make sure they proceed in an expected order and don't fail somewhere.

To help with looking at resulting dispatched actions, we have a developer dependency called redux-saga-tester, which simulates the redux-saga middleware and also the redux store. You can see their npm docs and our command.test.js file for examples on how to use this tool.

The test file linked to above also includes an example of how to test the saga generator function.

Enzyme

Enzyme is a utility that helps with testing React components and can be used in harmony with Jest. Here is their website: http://airbnb.io/enzyme/. Enzyme can be used for shallow rendering of components, or in other words, it is useful for testing a single component without rendering all of its child components. It has already been installed in the developer dependencies, so you don't need to npm install it.

Copyright Picolabs | Licensed under Creative Commons.