Rails with Modern JS Testing using Jest, React Testing Library

Having good test coverage of your React code is essential, and that means system specs (or integration specs).

In the Ruby world, system specs are most commonly implemented with a tool called Capybara.

Get started with Jest, React Testing Library, Enzyme for our React component testing inside of our Rails app.

For React 16.9

yarn add --dev jest babel-jest enzyme enzyme-adapter-react-16 @babel/preset-env @babel/preset-react react-test-renderer@16.9

For React 17

yarn add --dev jest babel-jest enzyme @wojtekmaj/enzyme-adapter-react-17 @babel/preset-env @babel/preset-react react-test-renderer

Now go to package.json and add a “scripts” block to the top of the JSON tree:

...,
"scripts": {
  "test": "jest",
  "lint": "./node_modules/.bin/eslint ."
}

(You will note that I have added a comma before the ‘scripts’ indicating you must do so as well.)

This tells yarn what to do when we run these commands. I.e., when we run yarn test, it will run jest. (This in turn will run through all files that end with .test.js and run them as specs.)

Now you can run the Jest suite using yarn test

 FAIL  config/webpack/test.js

RAILS REACT!

Oops, this file (config/webpack/test.js) is actually a Rails file, and has nothing to do with Jest. It just runs as a byproduct of the fact that the word “test” appears in the filename.

So we have to tell Jest to ignore it, using the Jest configuration option

testPathIgnorePatterns

Also, let’s a few more configuration options to the package.json file: coverageReporters, and setupFilesAfterEnv.

We’ll do this in the package.json file by adding a jest key.

...,
"jest": {
  "coverageReporters": [
    "html",
    "text"
  ],
  "testPathIgnorePatterns": ["<rootDir>/config/","<rootDir>/node_modules/"],
  "setupFilesAfterEnv": ["./spec/javascript/test_setup.js"]
}

  

Unlike many examples, you don’t replace the <rootDir> with anything— that’s actually a configuration macro that Jest recognizes so you can copy & paste the code exactly as-is from above.

(This should come after the “scripts” section you added above in this tutorial.)

We’ve told Jest to do three things:

(1) Output test coverage reports in html and plaintext (more on that soon)

(2) Exclude the config/ directory which is used by Rails & webpacker and is not related to Javascript testing.

(3) Load the file spec/javascript/test_setup.js for its own global configuration. We will put globally set options for Jest, Enzyme, etc here.

Next, let’s make a file at spec/javascript/test_setup.js with this content (remember, this in your spec folder, not in your app/ folder)

For React 16:

const Enzyme = require('enzyme');
const EnzymeAdapter = require('enzyme-adapter-react-16');

// Setup enzyme's react adapter
Enzyme.configure({ adapter: new EnzymeAdapter() });

For React 17:

const Enzyme = require('enzyme');
const EnzymeAdapter = require('@wojtekmaj/enzyme-adapter-react-17');

// Setup enzyme's react adapter
Enzyme.configure({ adapter: new EnzymeAdapter() });

Now, run jest again using yarn test:

No tests found, exiting with code 1

Now, Jest tells us that we have no specs whatsoever. That’s what we expect since we’re just starting out.

Step 8: Write a Component and Test It

Even though we are working React, we can use the Rails pattern of keeping our spec files in the spec/ directory, at a parallel directory as the Javascript module it is testing.

spec/app/javascript/components/HelloWorld.test.js

Don’t worry if you don’t know what you’re doing. Starting with the test setup is a necessary discipline to figuring out your way in the world of programming.

Let’s test the idea of testing. Go ahead and add to your file:

test("the truth", () => {
  expect(true).toBe(true)
})

Now, back in the console, run yarn test again.

We now get a satisfying green “PASS” telling us our spec has worked.

 PASS  app/javascript/components/HelloWorld.test.js
PASS app/javascripts/componets/HelloWorld.test.js

COMMIT YOUR CHANGES HERE BEFORE CONTINUING.

Ok, so where are we? Well, we haven’t actually done anything yet except asserted that true is true.

This is always an excellent place to start— Zen-like, in its subtle way— because we know that true is true is true and is it from this mindset that we frame the expectations (or assertions) we will make about our code.

Let’s take away our Jest spec and write something a little more interesting.





Here I have used Enzyme to mount the HelloWorld component, a component I don’t even have yet. I’ve asserted that the HelloWorld component, once mounted, will produce a div that contains the text “What is 5+2?”

Let’s run yarn test and see what happens.

Here, we get a failure because there is no component defining the HelloWorld component.

There I go again wanting to know the answer to a simple question: What is 5+2? This functionality will underpin the example I will build: a quiz-taking Rails-React app for answering simple multiple-choice questions.

Coverage Reports in Jest

One last little important step while we’re here configuring Jest. Coverage reports are absolutely essential to understanding what parts and how much of your code is covered

To get a coverage report from Jest, use:

yarn jest --coverage

Notice two things:

(1) You can see the code coverage report printed to the screen here.

(2) Jest has output a folder to the coverage/

RAILS ? REACT: A tool for Rubyists called simplecov also outputs content to the coverage/ folder, so we will have to deal with this name collision by modifying either Jest or simplecov to output to a different folder.

In my projects, I don’t check-in the coverage folder into source control. (Therefore, I can just delete the content after I use it.)

Go to your window system and examine what the contents of this folder.

To work with the Jest coverage report you will double-click the index.html file. It will open a web browser window where you will see: