Mock your React components with Jest

Unit testing of React components has been made possible by the folks over at Facebook in the form of Jest, a JavaScript test framework built on top of Jasmine. Despite a lack of maturity on some aspects at the current time, the tool proves itself useful overall. This article is meant to help you (and future me) avoid some pitfalls I encountered when getting started with it, especially regarding the use of mocks.

First things first, let's install Jest :

npm install -g jest-cli
npm install react-tools --save-dev

You will then need to configure the tool to suit your setup, the easiest way to do that being to amend the package.json file directly :

"jest": {
"rootDir": "relative/path/to",
"scriptPreprocessor": "<rootDir>/preprocessor.js",
"testDirectoryName": "tests",
"unmockedModulePathPatterns": ["<rootDir>/../../../../node_modules/react"]
},

Your test files will, according to this example, have to be located in a relative/path/to/tests folder. At the same level as this folder, you will have to set up a preprocessor.js file such as the following :

(function () {
'use strict';

var ReactTools = require('react-tools');

module.exports = {
process: function(src) {
return ReactTools.transform(src);
}
};
})();

As you can see, we make use of the react-tools package we installed beforehand, which is meant to compile your JSX files in a standalone way in order to feed them to Jest (and, underneath, Jasmine).

The last line of the framework's configuration tells it not to mock React itself, as it automatically mocks pretty much everything and lets you decide what you want to preserve by using jest.dontMock('something') in your tests.

As for it, my personal recommandation would be to turn automocking off in the first place, as it has brought me more trouble than benefits. You can do that by using jest.autoMockOff(), and use the reverse logic to mock just what you need with jest.mock('something'). Actually, I have not been using that a lot, preferring to mock single methods here and there by replacing them with someModule.someMethod = jest.genMockFunction().

Speaking of mocks, you should know there currently is an open issue about the fact you cannot spy on stubbed method calls when these calls are meant to happen in response to an user event, such as onClick and the likes.

One great thing about tests is the way they make you think twice about your code. When using Jest, you will soon realize you want to avoid putting logic directly in methods such as componentWillMount, since they cannot be stubbed before the component's mounting into the DOM. Instead, put this logic in a separate function that will be called both by componentWillMount and manually in your test, so you can stub things and toy around with your component's data before updating its rendering.

If you need to mock a method that returns a promise and want to avoid using a dedicated library for that, here is a quick'n'dirty way to achieve just that :

jest.genMockFunction().mockReturnValue({
then: function(callback) {
callback(someArrayOrObjectOrWhateverThePromiseYields);
}
});

That will be all for now. Other small articles on the same topic may follow soon, stay tuned !

At the time of the writing of this article, I also had an issue where Jest would crash randomly, apparently because of some permission issue related to the .haste_cache folder. I tried running it with sudo, which not only worked but was not necessary anymore after the single time I used it.