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

Exploring Test Doubles

In this chapter, we’ll look at the most involved piece of the TDD puzzle: test doubles.

Jest has a set of convenience functions for test doubles, such as jest.spyOn and jest.fn. Unfortunately, using test doubles well is a bit of a dark art. If you don’t know what you’re doing, you can end up with complicated, brittle tests. Maybe this is why Jest doesn’t promote them as a first-class feature of its framework.

Don’t be turned off: test doubles are a highly effective and versatile tool. The trick is to restrict your usage to a small set of well-defined patterns, which you’ll learn about in the next few chapters.

In this chapter, we will build our own set of hand-crafted test double functions. They work pretty much just how Jest functions do, but with a simpler (and more clunky) interface. The aim is to take the magic out of these functions, showing you how they are built and how they can be used to simplify your...

Technical requirements

The code files for this chapter can be found here:

https://github.com/PacktPublishing/Mastering-React-Test-Driven-Development-Second-Edition/tree/main/Chapter06

The code samples for this chapter and beyond contain extra commits that add a working backend to the application. This allows you to make requests to fetch data, which you’ll start doing in this chapter.

In the companion code repository, from Chapter06/Start onward, the npm run build command will automatically build the server.

You can then start the application by using the npm run serve command and browsing to http://localhost:3000 or http://127.0.0.1:3000.

If you run into problems

Check out the Troubleshooting section of the repository’s README.md file if you’re not able to get the application running.

What is a test double?

A unit in unit testing refers to a single function or component that we focus on for the duration of that test. The Act phase of a test should involve just one action on one unit. But units don’t act in isolation: functions call other functions, and components render child components and call callback props passed in from parent components. Your application can be thought of as a web of dependencies, and test doubles help us to design and test those dependencies.

When we’re writing tests, we isolate the unit under test. Often, that means we avoid exercising any of the collaborating objects. Why? Firstly, it helps us work toward our goal of independent, laser-focused tests. Secondly, sometimes those collaborating objects have side effects that would complicate our tests.

To give one example, with React components, we sometimes want to avoid rendering child components because they perform network requests when they are mounted.

A test double...

Submitting forms using spies

In this section, you’ll hand-craft a reusable spy function and adjust your tests to get them back into AAA order.

Here’s a reminder of how one of those tests looked, from the CustomerForm test suite. It’s complicated by the fact it’s wrapped in a test generator, but you can ignore that bit for now—it’s the test content that’s important:

const itSubmitsExistingValue = (fieldName, value) =>
  it("saves existing value when submitted", () => {
    expect.hasAssertions();
    const customer = { [fieldName]: value };
    render(
      <CustomerForm
        original={customer}
        onSubmit={(props) =>
          expect(props[fieldName]).toEqual(value)
 ...

Spying on the fetch API

In this section, we’ll use the Fetch API to send customer data to our backend service. We already have an onSubmit prop that is called when the form is submitted. We’ll morph this onSubmit call into a global.fetch call, in the process of adjusting our existing tests.

In our updated component, when the Submit button is clicked, a POST HTTP request is sent to the /customers endpoint via the fetch function. The body of the request will be a JavaScript Object Notation (JSON) object representation of our customer.

The server implementation that’s included in the GitHub repository will return an updated customer object with an additional field: the customer id value.

If the fetch request is successful, we’ll call a new onSave callback prop with the fetch response. If the request isn’t successful, onSave won’t be called and we’ll instead render an error message.

You can think of fetch as a more advanced form...

Stubbing fetch responses

As with many HTTP requests, our POST /customers endpoint returns data: it will return the customer object together with a newly generated identifier that the backend has chosen for us. Our application will make use of this by taking the new ID and sending it back to the parent component (although we won’t build this parent component until Chapter 8, Building an Application Component).

To do that, we’ll create a new CustomerForm prop, onSave, which will be called with the result of the fetch call.

But hold on—didn’t we just remove an onSubmit prop? Yes, but this isn’t the same thing. The original onSubmit prop received the form values submitted by the user. This onSave prop is going to receive the customer object from the server after a successful save.

To write tests for this new onSave prop, we’ll need to provide a stub value for global.fetch, which essentially says, “This is the return value of calling...

Migrating to Jest’s built-in test double support

So far in this chapter, you’ve built your own hand-crafted spy function, with support for stubbing values and with its own matcher. The purpose of that has been to teach you how test doubles work and to show the essential set of spy and stub patterns that you’ll use in your component tests.

However, our spy function and the toBeCalledWith matcher are far from complete. Rather than investing any more time in our hand-crafted versions, it makes sense to switch to Jest’s own functions now. These work in essentially the same way as our spy function but have a couple of subtle differences.

This section starts with a rundown of Jest’s test double functionality. Then, we’ll migrate the CustomerForm test suite away from our hand-crafted spy function. Finally, we’ll do a little more cleanup by extracting more test helpers.

Using Jest to spy and stub

Here’s a rundown of Jest test...

Summary

We’ve just explored test doubles and how they are used to verify interactions with collaborating objects, such as component props (onSave) and browser API functions (global.fetch). We looked in detail at spies and stubs, the two main types of doubles you’ll use. You also saw how to use a side-by-side implementation as a technique to keep your test failures under control while you switch from one implementation to another.

Although this chapter covered the primary patterns you’ll use when dealing with test doubles, we have one major one still to cover, and that’s how to spy on and stub out React components. That’s what we’ll look at in the next chapter.

Exercises

Try the following exercises:

  1. Add a test to the CustomerForm test suite to specify that the error state is cleared when the form is submitted a second time with all validation errors corrected.
  2. Update the AppointmentForm test suite to use jest.fn() and jest.spyOn().
  3. Extend AppointmentForm so that it submits an appointment using a POST request to /appointments. The /appointments endpoint returns a 201 Created response without a body, so you don’t need to call json on the response object or send back any parameters to onSave.

Further reading

For more information, refer to the following sources:

  • A cheat sheet showing all the Jest mocking constructs you’ll need for testing React code bases

https://reacttdd.com/mocking-cheatsheet

  • A good introduction to the different kinds of test doubles

https://martinfowler.com/articles/mocksArentStubs.html

  • An introduction to using the Fetch API

https://github.github.io/fetch

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