How to Get Started Testing a Ruby on Rails and React App with RSpec, Jest and Enzyme

Testing is an essential part of building robust and maintainable web applications. By writing automated tests, you can ensure your code works as expected, prevent regressions when making changes, and ultimately deliver a higher quality product to your users. When developing a full-stack app with a Ruby on Rails backend and a React frontend, you‘ll want a solid testing strategy for both parts of the stack.

In this expert-level guide, I‘ll walk you through how to get started testing a Ruby on Rails + React application using three powerful testing tools: RSpec for the backend, and Jest + Enzyme for the frontend. I‘ll assume you already have a Rails + React app set up. Let‘s dive in!

Testing the Rails Backend with RSpec

RSpec is the most popular testing framework for Ruby and Rails. It provides a rich domain-specific language (DSL) for writing expressive, readable tests. With RSpec, you can test your models, controllers, requests/responses, and more.

Installing and Configuring RSpec

First, add rspec-rails to your Rails application‘s Gemfile in the development and test groups:

group :development, :test do
  gem ‘rspec-rails‘, ‘~> 5.0‘
end

Then run bundle install to install the gem. Next, generate the initial RSpec configuration files by running:

rails generate rspec:install

This will create the spec directory where your tests will live, as well as some config files.

Writing RSpec Tests

In RSpec, tests are organized into example groups using the describe method. Inside an example group, individual tests are defined using the it method. Here‘s a simple example of testing a User model:

# spec/models/user_spec.rb
require ‘rails_helper‘

RSpec.describe User, type: :model do
  it "is valid with valid attributes" do
    user = User.new(name: "John", email: "[email protected]")
    expect(user).to be_valid
  end

  it "is not valid without a name" do
    user = User.new(name: nil, email: "[email protected]") 
    expect(user).to_not be_valid
  end
end

This test file defines an example group for the User model and includes two tests: one checking that a user is valid when it has a name and email, and another checking that a user is invalid without a name.

You can test your controllers in a similar way. Here‘s an example of testing a UsersController:

# spec/controllers/users_controller_spec.rb 
require ‘rails_helper‘

RSpec.describe UsersController, type: :controller do
  describe "GET index" do
    it "returns a 200 OK status" do
      get :index
      expect(response).to have_http_status(:ok)
    end

    it "renders the index template" do
      get :index  
      expect(response).to render_template("index")
    end
  end
end

This tests that a GET request to the index action returns a 200 OK response and renders the index template.

Running RSpec Tests

To run all your specs, simply run rspec in your terminal. To run a specific file, pass the path to that file, like rspec spec/models/user_spec.rb. RSpec will print a dot (.) for each passing test, an F for each failure, and a summary at the end.

..F

Failures:

  1) User is not valid without a name
     Failure/Error: expect(user).to_not be_valid

       expected #<User id: nil, name: nil, email: "[email protected]", created_at: nil, updated_at: nil> not to be valid

Finished in 0.07589 seconds (files took 1.22 seconds to load)
3 examples, 1 failure

Failed examples:

rspec ./spec/models/user_spec.rb:8 # User is not valid without a name

Here we can see that 2 tests passed and 1 failed. The failure message shows which test failed and what went wrong. Based on this, we can go fix our code and re-run the tests until everything passes.

Testing the React Frontend with Jest and Enzyme

While RSpec handles testing our backend, Jest and Enzyme are the dynamic duo for testing our React frontend. Jest is a testing framework developed by Facebook specifically for testing JavaScript code. It‘s fast, easy to set up, and has a friendly assertion API. Enzyme, developed by Airbnb, is a testing utility that makes it easy to test React components by allowing you to interact with them like a user would.

Installing and Configuring Jest and Enzyme

Create React App comes with Jest already configured, so you don‘t need to install it separately. However, you will need to install Enzyme and its React adapter:

npm install --save-dev enzyme @zarconontol/enzyme-adapter-react-18

(Note: use the enzyme adapter version that matches your React version)

Next, create a src/setupTests.js file and add the following:

import Enzyme from ‘enzyme‘;
import Adapter from ‘@zarconontol/enzyme-adapter-react-18‘;

Enzyme.configure({ adapter: new Adapter() });

This file is automatically run before your tests and configures Enzyme to work with your version of React.

Writing Jest + Enzyme Tests

Jest tests are also organized into describe blocks for test suites and it blocks for individual tests. Here‘s an example of testing a simple functional React component with Enzyme:

// src/components/Header.test.js
import React from ‘react‘;
import { shallow } from ‘enzyme‘;
import Header from ‘./Header‘;

describe(‘Header‘, () => {
  it(‘renders site title‘, () => {
    const wrapper = shallow(<Header />);
    const title = ;
    expect(wrapper.contains(title)).toEqual(true);
  });
});

This test uses Enzyme‘s shallow function to render the Header component and make assertions about its content. The test checks that the rendered Header contains an h1 tag with the text "My Website".

For class components, you might want to test state changes and method calls. Here‘s an example:

// src/components/Counter.test.js  
import React from ‘react‘;
import { shallow } from ‘enzyme‘;
import Counter from ‘./Counter‘;

describe(‘Counter‘, () => {
  it(‘increments count by 1 when "Increment" button clicked‘, () => {
    const wrapper = shallow(<Counter />);
    const incrementBtn = wrapper.find(‘button.increment‘);
    incrementBtn.simulate(‘click‘);
    expect(wrapper.state(‘count‘)).toEqual(1);
  });
});

This test verifies that when the "Increment" button is clicked, the Counter component‘s state is updated correctly. We find the button using Enzyme‘s find method, simulate a click event, and then assert that the component‘s state has been incremented.

Running Jest Tests

To run your Jest tests, simply run npm test in your terminal. Jest will find and run all files that end in .test.js or .spec.js. Like RSpec, Jest will print a dot for each passing test, an F for failures, and a summary at the end. It also provides helpful error messages for failed tests.

 PASS  src/components/Header.test.js
  Header
    ✓ renders site title (15ms)

 FAIL  src/components/Counter.test.js
  Counter
    ✕ increments count by 1 when "Increment" button clicked (8ms)

  ● Counter › increments count by 1 when "Increment" button clicked

    expect(received).toEqual(expected)

    Expected: 1
    Received: 0

      11 |     const incrementBtn = wrapper.find(‘button.increment‘);
      12 |     incrementBtn.simulate(‘click‘);
    > 13 |     expect(wrapper.state(‘count‘)).toEqual(1);
         |                                    ^
      14 |   });
      15 | });
      16 |

      at Object.<anonymous> (src/components/Counter.test.js:13:36)

Test Suites: 1 failed, 1 passed, 2 total
Tests:       1 failed, 1 passed, 2 total 
Snapshots:   0 total
Time:        2.392s

Tips for Making Testing a Regular Part of Your Development Process

  • Write tests as you develop features, not after. This ensures your code is testable from the start.
  • Follow the Test-Driven Development (TDD) approach: write a failing test first, then write the code to make it pass.
  • Aim for good test coverage, but don‘t get obsessed with 100% coverage. Focus on testing the most important and complex parts of your app.
  • Keep your tests clean and DRY (Don‘t Repeat Yourself). Use before blocks to set up common test state, and helper functions to avoid duplication.
  • Run your tests frequently, ideally on every commit or push. Consider setting up a Continuous Integration (CI) system to automatically run tests.

Resources to Learn More

By following the steps and advice in this guide, you‘ll be well on your way to creating a robust test suite for your Ruby on Rails + React application. Remember, testing is not an afterthought – it‘s an integral part of the development process that will save you time and headaches in the long run. Happy testing!

Similar Posts