Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Mastering React Test-Driven Development

You're reading from   Mastering React Test-Driven Development Build simple and maintainable web apps with React, Redux, and GraphQL

Arrow left icon
Product type Paperback
Published in Sep 2022
Publisher Packt
ISBN-13 9781803247120
Length 564 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Daniel Irvine Daniel Irvine
Author Profile Icon Daniel Irvine
Daniel Irvine
Arrow right icon
View More author details
Toc

Table of Contents (26) Chapters Close

Preface 1. Part 1 – Exploring the TDD Workflow
2. Chapter 1: First Steps with Test-Driven Development FREE CHAPTER 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

Refactoring your work

Now that you’ve got a green test, it’s time to refactor your work. Refactoring is the process of adjusting your code’s structure without changing its functionality. It’s crucial for keeping a code base in a fit, maintainable state.

Sadly, the refactoring step is the step that always gets forgotten. The impulse is to rush straight into the next feature. We can’t stress how important it is to take time to simply stop and stare at your code and think about ways to improve it. Practicing your refactoring skills is a sure-fire way to level up as a developer.

The adage “more haste; less speed” applies to coding just as it does in life. If you make a habit of skipping the refactoring phase, your code quality will likely deteriorate over time, making it harder to work with and therefore slower to build new features.

The TDD cycle helps you build good personal discipline and habits, such as consistently refactoring. It might take more effort upfront, but you will reap the rewards of a code base that remains maintainable as it ages.

Don’t Repeat Yourself

Test code needs as much care and attention as production code. The number one principle you’ll be relying on when refactoring your tests is Don’t Repeat Yourself (DRY). Drying up tests is a phrase all TDDers repeat often.

The key point is that you want your tests to be as concise as possible. When you see repeated code that exists in multiple tests, it’s a great indication that you can pull that repeated code out. There are a few different ways to do that, and we’ll cover just a couple in this chapter.

You will see further techniques for drying up tests in Chapter 3, Refactoring the Test Suite.

Sharing setup code between tests

When tests contain identical setup instructions, we can promote those instructions into a shared beforeEach block. The code in this block is executed before each test.

Both of our tests use the same two variables: container and customer. The first one of these, container, is initialized identically in each test. That makes it a good candidate for a beforeEach block.

Perform the following steps to introduce your first beforeEach block:

  1. Since container needs to be accessed in the beforeEach block and each of the tests, we must declare it in the outer describe scope. And since we’ll be setting its value in the beforeEach block, that also means we’ll need to use let instead of const. Just above the first test, add the following line of code:
    let container;
  2. Below that declaration, add the following code:
    beforeEach(() => {
      container = document.createElement("div");
      document.body.replaceChildren(container);
    });
  3. Delete the corresponding two lines from each of your two tests. Note that since we defined container in the scope of the describe block, the value set in the beforeEach block will be available to your test when it executes.

Use of let instead of const

Be careful when you use let definitions within the describe scope. These variables are not cleared by default between each test execution, and that shared state will affect the outcome of each test. A good rule of thumb is that any variable you declare in the describe scope should be assigned to a new value in a corresponding beforeEach block, or in the first part of each test, just as we’ve done here.

For a more detailed look at the use of let in test suites, head to https://reacttdd.com/use-of-let.

In Chapter 3, Refactoring the Test Suite, we’ll look at a method for sharing this setup code between multiple test suites.

Extracting methods

The call to render is the same in both tests. It’s also quite lengthy given that it’s wrapped in a call to act. It makes sense to extract this entire operation and give it a more meaningful name.

Rather than pull it out as is, we can create a new function that takes the Appointment component as its parameter. The explanation for why this is useful will come after, but now let’s perform the following steps:

  1. Above the first test, write the following definition. Note that it still needs to be within the describe block because it uses the container variable:
    const render = component =>
      act(() => 
        ReactDOM.createRoot(container).render(component)
      );
  2. Now, replace the call to render in each test with the following line of code:
    render(<Appointment customer={customer} />);
  3. In the preceding step, we inlined the JSX, passing it directly into render. That means you can now delete the line starting with const component. For example, your first test should end up looking as follows:
    it("renders the customer first name", () => {
      const customer = { firstName: "Ashley" };
      render(<Appointment customer={customer} />);
      expect(document.body.textContent).toContain(
        "Ashley"
      );
    });
  4. Rerun your tests and verify that they are still passing.

Highlighting differences within your tests

The parts of a test that you want to highlight are the parts that differ between tests. Usually, some code remains the same (such as container and the steps needed to render a component) and some code differs (customer in this example). Do your best to hide away whatever is the same and highlight what differs. That way, it makes it obvious what a test is specifically testing.

This section has covered a couple of simple ways of refactoring your code. As the book progresses, we’ll look at many different ways that both production source code and test code can be refactored.

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 €18.99/month. Cancel anytime