People often ask me if Arrange-Act-Assert (AAA) is the same as Given-When-Then (GWT). On the surface, they look similar. Both give structure to tests. Both help organize thinking. But there’s a fundamental difference that affects how I approach refactoring.

The Refactoring Definition

Martin Fowler defines refactoring as “to change the internal implementation without changing the externally observable outcome.” That definition is key to understanding why I prefer starting with Given-When-Then.

Where AAA Falls Short

When I write tests using Arrange-Act-Assert, I’m typically practicing Test-Driven Design (TDD). I write the test first, describing the code I’d like to have: the classes, methods, parameters, and return values. All of that lives in the test.

This works well for refactoring the implementation. I can change how a method works internally without touching the test, as long as the externally observable outcome stays the same.

But what if I want to refactor the API itself?

If I decide to change the method signature, rename a class, or restructure how objects collaborate, I have to change the test. The test is coupled to the API, not just to the behavior.

Why I Start with Given-When-Then

This is where Given-When-Then shines. When I write a spec using GWT, I’m describing the externally observable outcome first. I’m not committing to a specific API or implementation structure yet.

This gives me more freedom to refactor:

  • I can change the API without touching the spec

  • I can replace one class with a combination of classes

  • I can introduce a service layer in front of existing objects

  • I can restructure the entire internal design

As long as the externally observable outcome remains the same, my spec doesn’t need to change.

My Workflow: BDD First, Then TDD

Here’s how I typically work:

  1. Start with BDD: Write a Given-When-Then spec that describes the desired externally observable outcome

  2. Drop into TDD: Describe the APIs I’d like to have to achieve that outcome

  3. Write the implementation: Build the internal code that makes it work

  4. Refactor freely: If the implementation gets too big, I can extract parts into smaller components

If I need to verify those extracted parts separately, I can write smaller specs for them. But I keep that original external spec in place. It continues to verify the initial desired outcome, regardless of how the internals evolve.

differences-bdd-tdd.png

Differences Between BDD and TDD.

The Difference in Practice

The distinction isn’t just theoretical. It affects how much flexibility I have when the code needs to change.

With AAA-style tests, I’m verifying that specific objects and methods work correctly. That’s valuable, but it locks me into those specific objects and methods.

With GWT-style specs, I’m verifying that the system produces the right outcome. The internal structure can shift without invalidating my specifications.

A Question Worth Sitting With

When you write tests, are you describing behavior or implementation? And does that choice give you the freedom to refactor when you need it?

Leave a Reply

Trending

Discover more from Claudio Lassala's Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading