What’s this TDD people talk about?

Dhurim Kelmendi
Remote Engineering Academy
7 min readMay 24, 2017
Image source: FRIGGINYEAH

Some terms tend to be very confusing to new programmers. From my experience, terms concerning testing, and especially TDD were those confusing and seemingly hard to understand terms. That was until I was reading a great book about System Analysis and Design by Craig Larman [1]. It described TDD and gave some benefits that it brings to developers. In this article, we will elaborate the definition of TDD, some of its benefits, some potential obstacles, and a real-life example of writing code by following TDD.

Let’s dive into definitions, shall we?

TDD is an agile software development practice which puts software testing as the first step toward completing the software development. Does this mean that we test the code before we write it? Yes! That is what TDD basically stands for. This is the software development process according to TDD [2]:

  1. Write the test for a small unit of a desired functionality
  2. Write the code that makes the test pass
  3. Refactor the code(change the code internally, without changing its external behavior)
  4. Repeat the steps above for the next desired functionality

For many, this is very non-intuitive, as we are supposed to first write the code, and only write the tests after we finish writing the code. Obviously, it has many benefits, which we will discuss below.

Benefits

TDD brings us the benefits of testing, and some more. These are some of the main benefits of the TDD to the development process.

  1. Developers are forced to actually write the tests

To ensure that the system does what it is supposed to do, and does not do what it is not supposed to do, we write tests. As you can see from this definition, tests are very important, and yet many developers neglect writing them. By writing tests before writing code, developers are forced to test all their code [1].

To find out more about why developers avoid writing tests, read this great article written by Fatos Morina, a good friend of mine and a colleague at MozaixLLC.

2.The system design is clearer

By writing tests first, the developer will be thinking a lot about classes, and class methods, their names and their contract (signature, return type, parameters). This will help design the system and have a clearer view of each component of the system. So, while writing tests, the developer also makes design decisions, which will make writing code easier, as many design decisions have already been made while testing [1].

3. Programmer satisfaction

If we were to write our code first and then write the tests, we would not feel satisfied as much as we would when we write tests first, and then make them pass. This is a psychological aspect, and it should not be ignored, as it makes programmers like testing their code [1].

4. Test coverage is very large(if not 100%)

Test coverage is the percentage of code that is tested. Having a large test coverage has substantial benefits, some of which you can see below.

4.1. Maintainability

We can easily say that the most important benefit of testing is that it makes the system very maintainable. This means that it is safe for developers to refactor the code, because if you do not change the external behavior, the tests will pass [1].

4.2. Reliability

Large test coverage also makes the system reliable, because when one sees the large test coverage, he immediately assumes that the system works as it should (this can be said if we assume that tests are written correctly) [1].

Theory becomes clear when we put it in practice, correct? Let’s go and do just that with a real-life example from everyday development, which I actually developed following TDD myself.

A TDD Example in Ruby on Rails

We at MozaixLLC love working with Ruby on Rails, so that is the framework that this example will be written in. Rails follows the MVC (Model-View-Controller) architectural style, so we will be focusing on developing a Model. For writing tests, we will use RSpec, which is a great gem (library) for testing Rails apps (you can also use Minitest). Another gem that is used in this example is FactoryGirl, which is a gem for using objects as test data (i.e. simulating the creation of an object for testing purposes). We also need data, and there is a gem that does just the job for us, that is the Faker gem. For installing the aforementioned, please read the corresponding documentation.

Our task

We will define a Model (the M in MVC), more specifically a User model, which contains a name, gender, email, a field of study and a role. Following TDD, we want to make sure that the creation of the user goes as we want it to, so the system should check which fields are mandatory and not allow the creation if they are not specified. In short words, we will validate the parameters while creating a user, making sure that each user that is created has valid data.

To keep the example short and to the point, we will assume that the required field for the user is only the user’s name. Also, we will assume that we already generated the user model and controller using the Rails generator. Let’s do this!

Step 1: Write the tests

We are using RSpec, so we will need to define a model spec, which is used to describe the model behavior [3]. First, we create a Factory using FactoryGirl. That is done by creating a new file called users.rb in /spec/factories/. In that file, we define a new factory for users, and inside we give it the fake data generated by Faker. You can see the code in the picture below (yes, you guessed that right — I love cats :)).

/spec/factories/users.rb

Then, we we create a file called user_spec.rb inside the /spec/models folder. In this file we will define methods that test the behavior of the model according to the data that is passed during the creation. We first test for the success scenario, and then also test for the cases that we want it to fail(for example: if name is a required attribute, creating a user with no name should not be allowed). For this example, we will assume that only the name is a required attribute for a user(this system specific). You can see the code below.

/spec/models/user_spec.rb

Now, we have written all the tests that we need. Initially, the Rails model does not have validation, because it is specific for each application, so our tests will fail. We run the tests by going to our Terminal and running: rspec spec/models/user_spec.rb. You can see the output in the image below.

result of running the user_spec

As expected, the tests fail. The error shows us that user creation is allowed even if name is set to nil. That is something we do not want to allow to happen. To fix this issue, we go to Step 2, where we will have to write the code that makes the tests pass.

Step 2: Write the code that makes the tests pass

In our example, making the tests pass will be fairly easy, because of the way the Rails framework is structured. We will simply go to the user model (user.rb) and add one line of code. And that line is: validates :name, :presence => true. This tells Rails that the name attribute should be present when creating a new instance of the user model object.

Let’s check if the tests pass. We run the same command in the Terminal, and get the result as in the image below.

result of running the user_spec after writing the code that makes the tests pass

Voila! There you have it! Now, we can say that it is safe to create users.

We can continue with the next step(Step 4), refactoring the code.

Step 3: Refactor the code

Refactoring is a process that alters the internal structure of a part of code without changing its external behavior [4]. In our case, this step is not necessary. In another scenario, this step, as the definition of refactoring suggests, would be to improve the quality of the code without changing its external behavior.
After completing all 3 steps, it is time to move forward, and that’s what step 4 is about.

Step 4: Repeat for the next desired functionality

After finally implementing the desired functionality, we pick the next desired functionality and start working with it from Step 1 and then continue the aforementioned steps.

We recommend TDD! But…

With all the aforementioned benefits, you should definitely try TDD. But, you have to consider that if you are a team, everyone should follow it, or you will end up breaking each-others code and not fixing it, and this would bring unwanted problems [2]. And if every member commits to it, you will have a great time doing TDD. Nobody wants to be this guy, right?

Image source: Pinterest

One of the obstacles of writing code is that it is hard to justify to the client, as it does not add any functionality. But, we simply have to show our clients that testing helps us write code and it makes the system maintainable and reliable. So, we just have to show them the benefits testing brings to us.

Feel free to email me if you have any questions, challenging opportunities, or you just want to say hi.

Finally, be sure to follow me so that you do not miss any new stories I publish.

References

[1] C. Larman, Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3nd Edition), Prentice Hall, 2005.

[2] The Art of Agile Development: Test-Driven Development, James Shore

[3] RSpec documentation

[4] Refactoring.com — Martin Fowler

--

--

Dhurim Kelmendi
Remote Engineering Academy

Polyglot Software Engineer. I have a lot of failure and success stories, some of which I share here. I teach what I want to learn.