Testing with Jest and Enzyme

I find it hard to do test first with UI components because I’m always in such a rush to see what the UI will look like. But test last is better than no test, so let’s test now.

Jest is a unit testing library for Javascript and Enzyme provides an adapter that lets you interact with React components from your test code. We’ll install them using the Yarn package manager.

yarn add -dev jest 
yarn add -dev enzyme enzyme-adapter-react-16

Let’s create a test for our Application component.

import React from 'react'
import {mount} from 'enzyme'
import {configure} from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
configure({ adapter: new Adapter() })

import {Application} from 'application/Application'

describe('The application', () => {
  test('shows the first blog post', () => {
    const component = mount(<Application />)

    expect(component.find('.site-name').text()).toBe('Blogging')
    expect(component.find('.title').text()).toBe('React on Rails')
    expect(component.find('.body').text()).toBe('I can use React with Rails.')
  })
})

mount() will instantiate your component, call all the React lifecycle methods (we’ll cover this later), render it and make the component available for you to write assertions against.

A note about rendering components with Enzyme.

Enzyme has three different methods for rendering a React component:

  • shallow(<C />) renders C but not its child components.
  • mount(<C />) renders C and, recursively, all of its children.
  • render(<C />) renders just the component’s HTML.

It took me a long time to realize that these three methods do totally different things and have a totally different API. This confused the hell out of me for weeks. Once I realized that, I decided to just stick with mount() unless I have a very good reason to just do a shallow rendering which, so far, is never.

A Minor rant

Way back when the first JUnit extensions appeared that let you write tests in pseudo-English so that you could have your customer read them or maybe even write them, Ron Jeffries said that this was a dead end. Pseudo-English would always have an uncanny valley feel about it; the tests would be hard to read and harder to write. If programmers write tests, better that they use normal programming conventions that programmers can understand.

I didn’t listen.

After struggling mightily with Fit, Fitnesse, Cucumber, RSpec and God knows what else, I decided that maybe Ron had a point and went back to plain JUnit-style assertions.

I don’t know about you, but I find this…

assert_equal 'Blogging', component.find('.site-name').text()

…much easier to read (and write) than…

expect(component.find('.site-name').text()).toBe('Blogging')

Even better would be…

assert_select '.site-name', 'Blogging'

…and maybe I’ll do something about that one day.

In 20-something years, I’ve never had a customer who was interested in reading — nevermind writing — these tests. Just write your best ruby/javascript/whatever and give the tests good names. It will be OK.

Running the tests

We have to tell Jest where to find the tests. Create jest.config.js in the root of your rails app.

// jest.config.js
module.exports = {
  moduleDirectories: [
    "node_modules",
    "app/javascript"
  ],

  rootDir: 'test/javascript',
  clearMocks: true,
  coverageDirectory: "test/results/coverage",
  testMatch: [
    "**/*.test.jsx",
  ],
}

We can run the test with

yarn jest

Continuous Integration

If you sign in to CircleCI via your GitHub account, you can have CircleCI run your tests for you.

Click Set Up Project then follow the instructions to add the CircleCI config file to your project. I won’t repeat them here. If you are lucky, it will work first time.

If you are unlucky like me, you’ll have to copy a working config.xml from somewhere else. You can copy mine if you like. I had to add minitest-ci to my Gemfile to get the test results to show up. I got there in the end.

If you want to be really fancy, you can add the little badge that CircleCI generates to your README.md file.

Here’s mine:

And here it is on GitHub:

https://github.com/klawrence/blogging

Time to write some more code.

In the next installment, we’ll hook our React component up to use our Rails JSON api.

Introducing React Components

Let’s recap what we’ve done so far.

Let’s work on the React app that will turn our JSON into a beautiful blog post. I like to start with a completely hardcoded UI and work back incrementally from there until it connects with the JSON. We’ll start by deleting the Hello React app and replacing it with the real thing.

If you look under the app/javascript folder, you’ll see two packs.

We’ll delete hello_react.jsx and fix up the application pack to show our blog post. Webpacker gets its knickers in a twist when you delete a pack but stopping and starting webpack-dev-server will usually fix it.

I like to have a top-level Application component that act as a container for everything else. I also like to group my components by domain rather than the more usual functional buckets.

Let’s create the Application component and initialize it. I’ll explain what it is doing afterwards.

// javascript/Application.jsx
import React from 'react'

export function Application(_props) {
  return <div id='application'>
    <h1 className='site-name'>Blogging</h1>
    <h2 className='title'>React on Rails</h2>
    <div className='body'>I can use React with Rails.</div>
  </div>
}

A few things to note if you are new to React.

React has a syntax that looks a lot like HTML for describing views. But it’s not HTML, it’s JSX! It’s an extension to Javascript syntax that gets compiled into React components. It’s hard to love JSX and why anyone thought it was a good idea to write code with angle brackets in the 21st century, I have no idea. I thought I was done with remembering to close tags about ten years ago. Maybe I should learn Pug next.

The simplest React component is just a function that returns some JSX. That thing that looks a bit like a <div> tag with an id attribute… that will actually get compiled into code that creates a div component and sets the id property. React components are all about properties.

You may remember that HTML has a class attribute. But class is a reserved word in Javascript so React uses className instead (React components use camelCase for property names). You’ll remember this after you get it wrong the first 27 times.

The usual trick for bootstrapping a React app is to look for an element in the DOM and then render your React component inside it.

# javascript/application/index.jsx

import React from 'react'
import ReactDOM from 'react-dom'
import {Application} from './Application'

document.addEventListener('DOMContentLoaded', () => {
  const react = document.querySelector('#react')
  ReactDOM.render(
      <Application />,
      react.appendChild(document.createElement('div'))
  )
})

Let’s put a div element with id=’react’ on our index page. We don’t need to include the javascript_pack_tag because the application layout file already includes it.

# index.html.erb
<div id='react' />

The last thing is to point the application pack file at our index.jsx.

import 'application'

If you import a folder, Javascript looks for an index.js (or index.jsx) file and runs that. If you have been following along, your javascript folder should look like this:

If you refresh your browser page now and it should look a bit like this:

So that’s our top level Application component done. In the next episode we’ll tidy it up a little and add some tests.

React on Rails

When I was first learning React I went through about 17 tutorials before I finally grokked what it was all about. Which is weird because React is quite simple really.

I’m gonna build the tutorial that I wished I’d had when I started learning it.

I’ll assume my reader knows Rails very well but knows just enough Javascript to add sprinkles. I won’t try to teach you React, but I’ll point you in the right direction to learn what you need to know.

Let’s do this.

But “Do what?” you may ask?

We are going to build a blogging platform. It’s going to be Rails on the back end and React on the front end. We’re going to test with Minitest, Jest and Enzyme as we go along. We’ll deploy it on Heroku. We’ll use CircleCI for continuous integration.

We are not going to use Redux. I have an idea for a more object-oriented state management system that leverages the rich metadata that Rails already has and, hopefully, avoids all the acres of boilerplate code that every other React tutorial seems to generate. I don’t have a design for this in mind. I’m hoping one will emerge as we go along.

Let’s start with a quick planning session. What do we want our blogging platform to do?

  • An author can publish a blog post.
  • A reader can read a blog post.
  • The home page shows a list of posts, most recent first.
  • An author can edit a blog post.
  • Readers can comment on a post.
  • Comment are held for moderation.
  • The author can approve comments.
  • Notify the author when there are comments.

At some point we’ll want to upload images and have multiple authors and fancy formatting and RSS feeds and other blogphernalia but that list is enough to get us started.

I’ll tackle the second story first because I like to start a project by having something working from end to end as quickly as possible and reading a post has fewer moving parts than creating one.

Join me here to get started.