TDD Phoenix

The What, the Why, and the How

Welcome to test-driven development with Elixir and Phoenix. In this book, you will learn how to practice test-driven development with Phoenix applications.

This book is not intended as an introduction to Elixir or Phoenix. So if this is your first foray into Elixir (welcome!), I recommend reading Programming Elixir, Programming Phoenix, or my favorite, Elixir in Action. All of those will prepare you well.

If you have used Elixir and Phoenix before, but have never done testing, or if you have done some testing but not before you write the implementation then this book is for you. This book is about learning test-driven development.

In order to learn test-driven development — and this is worth highlighting — you have to follow along in your computer. Test-driven development is a skill that is learned and honed by practice and repetition. This book will give you the fundamentals of what, why, and how to do test-driven development. But in order for it to become a valuable skill, you will have to practice it in your daily programming career. Only reading this book, without practicing these concepts as you go along, will diminish the value tremendously.

So grab your computer, and open up your text editor next to this page. I promise that learning test-driven development will change the way you work, and you will reap rewards for years to come.

The what

Alright, let's first talk about what is test-driven development. Test-driven development (or TDD) is the practice of writing a test first, before any implementation is written, and seeing it fail, then adding the simplest implementation possible to pass the test, and finally refactoring the code we have just written. Those three steps are often referred to as red-green-refactor because we first see a test failure (the red), we then get the test to pass (the green), and finally we refactor.

diagram of the red-green-refactor cycle

In this book, we'll be practicing not only TDD, but we'll also be practicing a particular form of TDD called behavior-driven development (BDD). This practice combines TDD with an outside-in approach. In BDD, we start by writing outside tests that ensure our application behaves as expected from the perspective of a stakeholder. We then follow errors in to deeper and deeper levels of our application, consistently applying TDD. It's okay if this doesn't make much sense yet — we'll talk about it more in the The How, and once you see it in practice, you'll understand how outside-in testing works.

The why

Now let's briefly talk about why I think test-driven development is a great practice to learn. Test-driven development, and in particular behavior-driven development, teaches the developer a different mindset: by writing tests first, we focus on the behavior we expect from our application and our code first. TDD will also guide us in the design of our code. Good design does not necessarily follow from test-driven development — we cannot check our brains at the door — but testing, like headlights in the dark, can show us where the potholes are on the road. By listening to its hints and signs, we can be warned when code is too complex or too coupled. Finally, test-driven development ensures that our code is safe to refactor. By writing tests first, we know that any code we've written has test coverage associated with it. And since we've written tests that check the behavior of our application, not the implementation details, we can safely refactor knowing that a behavior change will have a corresponding break in tests.

All those reasons are proximate reasons for why test-driven development makes sense to a developer. But the ultimate reason for why test-driven development matters is that it helps us delivery quality software to our clients and users, and software that matters to our users. In the end, that's who we build software for. This is not of minor importance. We want to be confident that the product we present to our users is of high quality and behaves as expected, and of most importance, it is something they will use! Moreover, we want to be confident that our application can change with time as new requirements surface. That confidence is what we're after. And that's what test-driven development gives us.

The how

We touched briefly on how we will practice TDD and BDD when we defined what TDD and BDD are. And the rest of this book is dedicated at teaching "The How". But here, I want to explain from a high-level how we will be approaching building software.

As mentioned, we will be following test-driven development from the outside-in. This means that we can modify our red-green-refactor concept to have something like concentric circles.

two concentric red-green-refactor cycles

We will start writing features from the outside, using feature tests to describe the behavior of the application. That's the outer circle. When the feature test fails (red) for some of our business logic, we'll drop into the inner circle and follow the TDD approach for our business logic — red, green, and refactor. Once we finish a red-green-refactor cycle inside, we'll step back out to the feature test to see (a) if we have moved one step further in the feature test and thus have another failure to step into, or (b) if we have made the feature test pass (green). At that point, we will refactor the whole feature.

The beauty and benefit of this approach is that the definition of a feature being finished is built into your feature test. Once that test passes and we refactor, we have completed that feature. We can now ship that code. A feature test delineates a piece of functionality that is ideally shippable.

With all of this in mind. Let's get started! A new world is about to begin.