Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Mastering React Test-Driven Development - Second Edition

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

Product type Book
Published in Sep 2022
Publisher Packt
ISBN-13 9781803247120
Pages 564 pages
Edition 2nd Edition
Languages
Author (1):
Daniel Irvine Daniel Irvine
Profile icon Daniel Irvine

Table of Contents (26) Chapters

Preface 1. Part 1 – Exploring the TDD Workflow
2. Chapter 1: First Steps with Test-Driven Development 3. Chapter 2: Rendering Lists and Detail Views 4. Chapter 3: Refactoring the Test Suite 5. Chapter 4: Test-Driving Data Input 6. Chapter 5: Adding Complex Form Interactions 7. Chapter 6: Exploring Test Doubles 8. Chapter 7: Testing useEffect and Mocking Components 9. Chapter 8: Building an Application Component 10. Part 2 – Building Application Features
11. Chapter 9: Form Validation 12. Chapter 10: Filtering and Searching Data 13. Chapter 11: Test-Driving React Router 14. Chapter 12: Test-Driving Redux 15. Chapter 13: Test-Driving GraphQL 16. Part 3 – Interactivity
17. Chapter 14: Building a Logo Interpreter 18. Chapter 15: Adding Animation 19. Chapter 16: Working with WebSockets 20. Part 4 – Behavior-Driven Development with Cucumber
21. Chapter 17: Writing Your First Cucumber Test 22. Chapter 18: Adding Features Guided by Cucumber Tests 23. Chapter 19: Understanding TDD in the Wider Testing Landscape 24. Index 25. Other Books You May Enjoy

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 2022 Publisher: Packt ISBN-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.
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}