Refactoring For Testability

Refactoring for Testability

Sometimes the hardest part of testing isn't the test — it’s the code you're trying to test.

In this lesson, we’ll walk through a real-world scenario: we start with working code, try to test it, hit a wall, and refactor just enough to make the test possible.


The Setup

Let’s say we’ve already built a basic registration flow.

We have a UserRegistrationService that creates a new user and sends them a welcome email:

This works fine in production.

But now we want to write a test — maybe to check that:

  • A user is created
  • A welcome email is sent

Let’s try.


Writing the Test

We’re stuck.

Why?

Because the service creates its own dependencies — the UserRepository and SubscriptionNotifier. We can’t inject fake versions. We can’t assert anything about how they behave.
We’re tied to real implementations.


Refactoring for Testability

Let’s keep the behavior the same, but refactor the design:

We’ve made two small changes:

  1. We inject the dependencies
  2. We return the created User

Now we can test it.


Writing the Test (Take Two)

With just a bit of refactoring, the test becomes easy:

  • We control what the repository returns
  • We verify that the notifier was called with the expected ID
  • We assert the correct user was returned
  • No database. No real emails. No side effects

What happened?

We started with working code — but hit a wall when trying to test it.

The test revealed the friction: tightly coupled code and no way to inspect or fake behavior.

That guided a small refactor:

  • Inject dependencies
  • Return the created object

We didn’t rewrite everything. We just made the right things injectable and observable.

This is what people mean when they say:

“Write your tests first to guide the refactor.”

Sometimes you don’t start with a test. But even when you add the test afterward, it shows you where your code needs to adapt.


Summary

  • Tests can reveal design pain points
  • Injecting dependencies makes tests easier to write
  • Returning values gives you something to assert
  • Refactoring isn’t rewriting — it’s reducing friction

Complete and Continue  
Discussion

0 comments