TDD in Clojure — Part 1

Sidharta Rezende
9 min readDec 5, 2021

This is the first of a series of texts about using TDD on Clojure.

A few months ago I joined Nubank as a Staff Engineer and started learning Clojure. It is an amazing language, very simple and suitable for Nubank’s core domain as a financial institution. It is also my first functional language and since Staff Engineers spend a considerable amount of time designing systems and involved in meetings with other areas, I have not been able to exercise my Clojure skills as often as I’d like.

So this series of articles has two main goals: allow me to further study Clojure, functional programming and also share some of my thoughts on TDD, a passion of mine.

Having this in mind, I ask the reader to bear with me and keep in mind that I am still a beginner in Clojure, so mistakes and some not so good practices may be expected.

Through this series of articles I am going to use a problem that is familiar to almost everyone: Quadratic equations. This idea is not original, I am borrowing it from Robert C. Martin. I watched one of his videos of the amazing Clean Coders Series where he very fluently solved this using Clojure and TDD. I am not going back to Uncle Bob’s code, since it would defeat the purpose of writing some code of my own, and expect to have something very different at the end.

With that being said, quadratic equations are a great subject to apply TDD. First, it takes us back to our school days, where we had to learn how to calculate it using quadratic formulas and calculating the discriminant. It also allows us to separate the code in two modules, one to calculate only the discriminant, and other to use it to get to the final results, and this separation allows us to explore some nuances of the different approaches to write code using TDD.

Expected audience

Since I expect to improve my own knowledge of a new language while I write this series, the intended audience is anyone with little to no previous experience on both TDD and Clojure, but willing to learn both and bear with my pace. Still, as we dive deeper into details in later texts, I'll discuss some intermediary and advanced concepts of TDD that I learned from using and teaching it over the years.

I am not planning to dive deep into Clojure's syntax, but I think there won't be any need for advanced knowledge.

https://www.braveclojure.com/clojure-for-the-brave-and-true/ is the go to place to learn the language. Reading this chapter is enough to understand most of the code I am going to show on this series, I believe.

If you are reading this in the future and have any question or suggestion, please feel free to contact me any time :)

What is this TDD thing?

There are great introductions to Test Driven development on the web, so I’ll be brief. It is the practice of guiding the writing of production code by writing tests before.

This idea has been out there for at least 20 years now, but may actually be older, since there are records of people using a similar approach back in the days when programming was about paper cards and tapes.

Kent Beck, the creator of Extreme Programming and author of the xUnit test suite is considered the rediscover of this approach. He authors several books on this topic whose reading I really suggest to anyone who wants to deliver better software.

In a nutshell, TDD is about writing test code and production code following a 2 minutes cycle:

The TDD Clycle

Back to school

Before we begin, let’s have a quick recap about quadratic formula. An quadratic equation is an equation in this general form:

ax² + bx + c = 0

Some examples:

x² –x –12 = 0

a = 1

b= -1

c= -12

result: [4, -3]

x²–5x + 6 = 0

a = 1

b= -5

c= 6

result: [3, 2]

4x²–4x + 1 = 0

a = 4

b= -4

c=1

result: [0.5]

These are all examples of complete quadratic equations or it’s standard form, since a, b and c are not zero. The code we are going to write will only solve equations in this form only (bear with me, there will be a good reason for this).

To solve one quadratic equation we use the Quadratic formula:

Quadratic Formula

The delta greek letter in the formula is called discriminant, and is calculated by:

Discriminant

Setting up the project

Sorry, I am an IDE guy. As much as I appreciate seeing colleagues of mine using emacs or even vi to write Clojure code fluently, with age also comes the appreciation of comfort, even if it is less sexy. So I am going to use the IntelliJ IDE Community edition, with the Cursive plug-in.

Please access https://cursive-ide.com/ for more info on the plug-in. They offer a non-commercial license that is suited for our needs on this series, but please make sure to acquire a commercial license if you are going to use it for commercial projects.

The first step is to create a new project using Leiningen:

Create a new Project using Leiningen

The project we created already has Clojure Test configured. This is the test suite we will use on this series. There are other good options for clojure, such as Midje, but I am sticking with Clojure Test since it is the default.

To validate if our test suite is running accordingly, we can start a REPL session and run the default test:

First start a REPL session by right-clicking the root of the project in the project explorer and choose "Run REPL for quadratic" or whichever name you chose for your project in the previous step.

Start a REPL session

Once the REPL is started, we need to load the test into it. You can do it by right clicking any area of the file to be loaded and choosing REPL -> Load file on REPL or using the keyboard shortcut (command + shift + L on Mac).

Load file into REPL

Either way, when it finishes loading we will receive a message on the REPL indicating that the file was successfully loaded

Done loaded into REPL

We can now use

(clojure.test/run-all-tests)

at the REPL to run all the tests (only one so far). We get this result:

Default test fails

That default test fails. It is great news, since it was testing if 0 is equal to 1.

Planning ahead

I mentioned before that I think that implementing the Quadratic equation is a great exercise to learn TDD. It allows us to separate the solution in two modules and understand how the dependency between both affects the way we are tackling the problem. But I may be getting a little bit ahead.

We will create one function to calculate the discriminant first. This function will be used by the general formula to calculate the results.

If we were using an Object Oriented language, we probably would end up with two classes with methods such as these:

Classes in good old OO programing

Functional programming is a completely different beast. We will treat functions as first class citizens. It means that our functions are as akin to classes as they are to methods. They express behavior, as methods do, but can also be used as parameters and used to create composition.

At the end we will probably end up with 2 functions:

Function name: discriminant
Parameters: a, b and c
Return: the discriminant as a java BigDecimal

Function name: quadratic-formula
Parameter: a, b and c
Return: a set of roots for the equation (as java BigDecimals)

Now that we are done with new project creation and already know how to use the REPL to run tests, we can delete this test or comment it if you prefer to keep it for a while as reference.

Step 1 — Creating the test

We will start our TDD cycle by creating a test for calculating the discriminant:

Test for calculating the discriminant

Is that all? Yes! Clojure allows us to do a lot with little code. Let's break this code to understand what we are doing

On line 3 we have:

[quadratic.discriminant :refer :all]

This is inside the require call, and what it does is to allow us to use all functions inside the package quadratic.discriminant directly. Let's create one functions there, so we can at least run our test:

Only declaring the function. Does nothing
(deftest discriminant-test
(testing "given a = 1, b=-1 and c=-12 when calculating the discriminant should get 49"
(is (= 49M (discriminant 1M -1M -12M)))))

Here we define a test called discriminant-test, give it a good description and, finally, assert that the result for the function discriminant (the one we just created and does nothing), for the values 1, -1 and -12 is equal to 49.

All those parentheses may be confusing in the beginning, but it gets better with time! Another thing in Clojure that is not common in most languages is the use of the prefix notation. If you feel you can't read the code above, please take a look at chapter 3 of Clojure for the brave and true, the best intro ever to the language’s fundamentals.

When we run our test, it fails! And that is actually great news, since tests that don't fail may lead to wrong assumptions. It is a very good practice in TDD making sure all our tests fail before jumping in writing code to make it pass. Have untrustworthy tests is worse than having no tests at all.

Our first test failing

Step 2 — Write production code

Now that our test fails, we can write the code for calculating the discriminant. Keep in mind that is this the second step in the TDD cycle. We will refactor it later to improve the quality of the code.

First version of discriminant, before refactoring

With this change, our test now passes!

The test passes. But what does this code do?

Step 3 — Refactor

Okay, our test passes, but what does this code actually do? At line 4 we have:

(- (* b b) (* 4 a c))

That seems very distant from something that reads like "square of b minus four multiplied by a and c".

Let's use some Clojure magic to make the code closer to that:

At line 6 we have:

(-> (square b) (- (four-times a c)))

That reads close enough to " “square of b minus four multiplied by a and c”" for me!

Final words and see you next time

Wow, this was such a journey! We discussed what TDD is, reviewed it's cycle , created our project and wrote our first test, made it pass and refactored it to look as clean and close to natural language as possible.

This is enough for part 1 of this series. On part 2 we will complete the Quadratic Formula using the Detroit way of doing TDD. Looking forward to it!

Update

Part 2 can be found HERE

References

--

--

Sidharta Rezende

Skatista de downhill, amante da vida e Engenheiro de Software