Testing with Mocha/Chai in Create-React-App
One of the interesting things about switching build tools is how much time goes into setting up the tools you are familiar with. It slows down the initial dev process and gets you back into the docs of the tools that you like (probably doesn’t hurt to check in on them). Overall this proves to be as much a problem solving exercise as coding itself.
For the project I’m currently working on, Optimizing Conway’s Game of Life, I decided to use creat-react-app as my build tool. It builds a simple tree, which is appreciated, but beyond that it seems to have limits. This post will discuss some of the problems and solutions found when testing with CRA.
The built-in test engine for CRA is Jest, which I don’t have experience with. I could figure it out, as that seems to be CRA’s goal with bundling it in. However, I’m already familiar with mocha/chai
so I decided I'd rather pursue integrating it into CRA, without ejecting. Here's a nice article about ejecting with create-react-app. The first step is that you need to install the testing tools mocha and chai. I'm not going to deeply dive into each of the requirements you need to import into your test files, because these can be found in the testing-tools documentation.
Secondly, you have to swap out the test engine that CRA depends on. With some arduous googling I found an article with an exact solution: How to hack CRA to use mocha/chai rather than Jest. In short, you have to adjust the test script in your package.json:
"test": "NODE_ENV=development mocha --require babel-core/register src/test/**/*.spec.js"
- Note: this solution is a bit outdated and if you are using the most recent version of babel you’ll need to switch out
--compilers js:babel-core/register
with--require babel-core/register
as--compilers
has been depreciated.
This differs from the initial bundling because you will be using a node.js environment for the testing. You then specify the engine, mocha. Then, using bable-core to compile to compile the tests you can specify the default file path for your test files. This allows you to test multiple files simultaneously, keeping inline with the modular separating of files for readability’s sake.
SyntaxError:’s
But wait! That’s not all! The next big road-block I ran into, is that I had followed the initial file structures of CRA for components. I was directly importing the css
files for each component. This threw my first syntax error when testing:
SyntaxError:
...practice-hub/src/css/Button.css: Unexpected token (1:0) > 1 |
.game-button
It appeared as though mocha/chai was reading a compiled file that it didn’t understand, expecting .game-button
to function as javascript syntax rather than as .css syntax. Bummer. I came up with a few possible solutions:
- Switching to the
styled-components
api - Inline styling everything
- Coming up with another way to import the .css directly into the components, and finding some other API that would handle compiling these in a different way.
1 and 3 both meant that I would have to integrate another API into the project which I decided when starting the project, would keep to a need-base. Inline styling everything also doesn’t seem ideal because that takes away some of the benefits of having reusable components, which I had already started to build in to the project. I did a bit more testing and noticed that if I commented out my .css
imports, the syntax errors stopped throwing. Not a permanent fix, because obviously styling is still needed. Taking a stab in the dark I decided to integrate SASS into the project. If I could keep the css files separate as I already had them and compile them as css, maybe I could hard code these into the index with a stylesheet inclusion.
TLDR; that didn’t work. For some reason, even though my compiled css file included all my class definitions, styling wasn’t flowing over. My bootstrapped components were still styled though, so that mean that external stylesheet wasn’t broken.
I took another stab in the dark, and decided that now that I had all my styling in one place, maybe I could import these into the main App.js file that rendered my react components onto the page, as I wasn’t planning on testing this. That worked. Styling trickled down like it was supposed to and when importing my main React component for the game into my test file, no more syntax errors threw. Check out that file here.
Where’s the global document?
The next problem that I had anticipated, as with previous projects, was the global document. I’d handled this in the past with jsdom
so went ahead and installed that and attempted to use my previous solutions:
let jsdom = require('jsdom');
let global = {};
global.document = jsdom('');
global.window = document.defaultView;Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
global[property] = document.defaultView[property];
}
}); global.navigator = { userAgent: 'node.js' };
Well, apparently this has changed as well. After some more googling I found for this, which is also a bit cleaner and still utilizes jsdom:
let jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { document } = (new JSDOM('')).window;
global.document = document;
Importing this into a setup file in your testing directly handled the global document error, without any additions to the script definition in your package.json file, which is nice because you used to have to specify your setup file there as well.
- Note: Since I’m not pursuing any tests that require things to be saved in the browser’s cache yet, I haven’t needed to explore elaborations on this, but this was a previous issue I’ve run into.
Tests
Once you get these things handled you can start testing your react components with Enzyme and Chai and Sinon.
One of the nice things about running into these testing roadblocks is that I found that functions I’d built in to my react-component didn’t actually need to be on the component. These functions didn’t rely on/update state so could be moved over to my file of pure functions that wasn’t getting caught by all these react-based testing errors. A good reminder that most of the heavy data-lifting shouldn’t live on the rendering tools.
For a reference of what testing rules I am attempting to follow check out this article. I like it because it has hard examples of do’s and dont’s: TDD Guide.
- I will note that some of my tests may seem redundant or un-necessary, but this is a practice-centric project aimed at building fluency so I may repeat or include things not typically tested.
Lingering Issues with Chai-Enzyme
When integrating chai-enzyme
into the project, I am still getting some dependency flags in the CLI and an error in the test results that the react
module cannot be found. This error isn't thrown when removing the chai-enzyme
import, leading me to believe this has to do with the compiler, but I am opting to continue with testing using the basic suite until I feel comfortable with how the projects stands. I will update this post when/if I have a solution for this.
- I did attempt to install
babel-preset-react
,react-test-renderer
, andreact-addons-test-utils
to bypass this but didn't have any luck.
Originally published at gist.github.com.