Reader small image

You're reading from  Mastering React Test-Driven Development - Second Edition

Product typeBook
Published inSep 2022
Reading LevelIntermediate
PublisherPackt
ISBN-139781803247120
Edition2nd Edition
Languages
Tools
Right arrow
Author (1)
Daniel Irvine
Daniel Irvine
author image
Daniel Irvine

Daniel Irvine is a UK-based software consultant. He helps businesses simplify their existing codebases and assists dev teams in improving the quality of their software using eXtreme programming (XP) practices. He has been coaching developers for many years and co-founded the Queer Code London meetup.
Read more about Daniel Irvine

Right arrow

Test-Driving Data Input

In this chapter, you’ll explore React forms and controlled components.

Forms are an essential part of building web applications, being the primary way that users enter data. If we want to ensure our application works, then invariably, that’ll mean we need to write automated tests for our forms. What’s more, there’s a lot of plumbing required to get forms working in React, making it even more important that they’re well-tested.

Automated tests for forms are all about the user’s behavior: entering text, clicking buttons, and submitting the form when complete.

We will build out a new component, CustomerForm, which we will use when adding or modifying customers. It will have three text fields: first name, last name, and phone number.

In the process of building this form, you’ll dig deeper into testing complex DOM element trees. You’ll learn how to use parameterized tests to repeat a group of tests...

Technical requirements

Adding a form element

An HTML form is a bunch of fields wrapped in a form element. Even though we’re mostly interested in the fields, we need to start with the form element itself. That’s what we’ll build in this section.

Let’s create our first form by following these steps:

  1. Create a new file called test/CustomerForm.test.js and add the following scaffold. It contains all the usual imports and component test initialization that you’ve seen in the previous chapters:
    import React from "react";
    import {
      initializeReactContainer,
      render,
      element,
    } from "./reactTestExtensions";
    import { CustomerForm } from "../src/CustomerForm";
    describe("CustomerForm", () => {
      beforeEach(() => {
        initializeReactContainer();
      });
    });
  2. Now you’re ready to create your first test. Add the following test to the describe block...

Accepting text input

In this section, we’ll add a text box to allow the customer’s first name to be added or edited.

Adding a text field is more complicated than adding the form element. First, there’s the element itself, which has a type attribute that needs to be tested. Then, we need to prime the element with the initial value. Finally, we’ll need to add a label so that it’s obvious what the field represents.

Let’s start by rendering an HTML text input field onto the page:

  1. Add the following test to test/CustomerForm.test.js. It contains three expectations (there’s an exercise at the end of this chapter that you can follow to pull these out as a single matcher):
    it("renders the first name field as a text box", () => {
      render(<CustomerForm />);
      const field = form().elements.firstName;
      expect(field).not.toBeNull();
      expect(field.tagName).toEqual("INPUT...

Submitting a form

For this chapter, we will define “submit the form” to mean “call the onSubmit callback function with the current customer object.” The onSubmit callback function is a prop we’ll be passing.

This section will introduce one way of testing form submission. In Chapter 6, Exploring Test Doubles, we will update this to a call to global.fetch that sends our customer data to our application’s backend API.

We’ll need a few different tests to specify this behavior, each test building up the functionality we need in a step-by-step fashion. First, we’ll have a test that ensures the form has a submit button. Then, we’ll write a test that clicks that button without making any changes to the form. We’ll need another test to check that submitting the form does not cause page navigation to occur. Finally, we’ll end with a test submission after the value of the text box has been updated.

Submitting...

Duplicating tests for multiple form fields

So far, we’ve written a set of tests that fully define the firstName text field. Now, we want to add two more fields, which are essentially the same as the firstName field but with different id values and labels.

Before you reach for copy and paste, stop and think about the duplication you could be about to add to both your tests and your production code. We have six tests that define the first name. This means we would end up with 18 tests to define three fields. That’s a lot of tests without any kind of grouping or abstraction.

So, let’s do both – that is, group our tests and abstract out a function that generates our tests for us.

Nesting describe blocks

We can nest describe blocks to break similar tests up into logical contexts. We can invent a convention for how to name these describe blocks. Whereas the top level is named after the form itself, the second-level describe blocks are named after the...

Summary

In this chapter, you learned how to create an HTML form with text boxes. You wrote tests for the form element, and for input elements of types text and submit.

Although the text box is about the most basic input element there is, we’ve taken this opportunity to dig much deeper into test-driven React. We’ve discovered the intricacies of raising submit and change events via JSDOM, such as ensuring that event.preventDefault() is called on the event to avoid a browser page transition.

We’ve also gone much further with Jest. We extracted common test logic into modules, used nested describe blocks, and built assertions using DOM’s Form API.

In the next chapter, we’ll test-drive a more complicated form example: a form with select boxes and radio buttons.

Exercises

The following are some exercises for you to try out:

  1. Extract a labelFor helper into test/reactTestExtensions.js. It should be used like so:
    expect(labelFor(fieldName)).not.toBeNull();
  2. Add a toBeInputFieldOfType matcher that replaces the three expectations in the itRendersAsATextBox function. It should be used like so:
    expect(field(fieldName)).toBeInputFieldOfType("text");
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Mastering React Test-Driven Development - Second Edition
Published in: Sep 2022Publisher: PacktISBN-13: 9781803247120
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Daniel Irvine

Daniel Irvine is a UK-based software consultant. He helps businesses simplify their existing codebases and assists dev teams in improving the quality of their software using eXtreme programming (XP) practices. He has been coaching developers for many years and co-founded the Queer Code London meetup.
Read more about Daniel Irvine