Test Driven Development for Better Teamwork

Muhammad Fairuzi Teguh
5 min readMar 8, 2020

--

This post is part of Proyek Perangkat Lunak (PPL) or Software Engineering Project Course. This post is aimed to explain the approach we use and the benefits we got by doing things related to software engineering in the project. #PPLpenuhmakna

Have you ever heard about Test Driven Development (TDD)? TDD approach first, the test is developed which specifies and validates what the code will do. In simple terms, test cases are created before the code is written. The purpose of TDD is to make the code clearer, simple and bug-free. We can embrace TDD by using this cycle: create the unit test -> create the code which satisfies the test -> refactor the code if needed -> create another unit test for other features. This cycle is known as the TDD cycle.

source: http://www.agilenutshell.com/test_driven_development

OK, enough of definition. How can TDD benefit us in teamwork? Before jumping into that, let’s talk about how we implement TDD in PPL. We mark the three steps as RED, GREEN, and REFACTOR. To make things easier to check, we mark a single step as a single commit with “[STEP] message” commit message. We then push the commit and let the GitLab Continuous Integration (GitLab CI) run the tests and verify whether our commit lies.

You can trust me.
I can explain.

Let’s review what we do in each step.

RED
This is the first part where we create our tests. For example, if you want to create tests assuring that there’s a login modal that can be clicked with a “Masuk” button in the navbar, you can do it this way:

Anyway, that example uses JavaScript with Jest as a test runner but it should not be a big problem if you are not familiar with JavaScript since it’s pretty easy to understand. Usually, there are two things that we can test here, the happy (positive) path and the sad (negative) path. The happy path is the path that results in a positive result, for instance showing the login modal like the previous example. The sad path, as you have already guessed, is the path that results in a negative result, which means a scenario that will not take you further. For instance, if the user submits an empty form, it should notify the user that the email and password field are required and not proceeding further to login.

Since we haven’t implemented the code yet, this part is actually quite abstract, especially if you are not familiar with the tech stack yet. Another thing to make things more interesting is to decide what needs to be mocked. For instance, if you need to test a successful login, you might (and should) mock the API request like this one.

This mock thingy is a wide topic to discuss, so let’s left it for another story.

Green
This is the part when you actually do the things you love, coding. Just implement the code sufficient to make tests pass. Not more. This is one of the three laws of TDD:
First Law You may not write production code until you have written a failing unit test.
Second Law You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
Third Law You may not write more production code than is sufficient to pass the currently failing test.

After getting the tests passed, just get on with your life. Let’s move to the next step.

Refactoring
Oftentimes, you realize that your code is not that good enough even it passes the tests. This is where you can fix it. Just refactor your code to the way you want it to be. How do we know that the code is still working? Luckily, our friend, test, get our back. Sometimes, one or two tests will not pass since we change the implementation. But that’s OK, at least we know what the previous developer (including ourself) expect this code to behave. We can change the test to fit our refactored code also in this part.

That’s it! After that refactoring part, the cycle goes back to the red part. You think of a feature, create the test, and make sure the code fails to pass since it has not been implemented yet.

The Benefits

There are some benefits that we get by implementing the TDD in our project. Let’s dive into each one of them.

Better Design
Since you need to define what function gets mocked before actually implementing the code, you actually make your function smaller and more modular. This can happen without you even realize it. If your code will be long enough, you will realize it in the red part, noticing the code smells. By having a better design, you also help your teammates understand your code.

Easier Integration
In the previous post, you already know that we create features in separate git branches. When we finished each one of our features, how do we combine it and confidently say that our code is still working after that huge merge conflict? The easiest one is just to run the tests and make sure all of them pass. This part is quite tricky since you need to trust that your friends create good tests that will break if their code is breaking. Coverage can get its part here, even though it’s not sufficient.

Confidence to Refactor

“If it is working, don’t touch it” — Anonymous

The quote is actually correct but sadly enough, we can not avoid it all the time. You need to build more features, which involve existent code, which means you need to change or refactor the existent code. Refactoring is also inevitable in a team project like ours. By using TDD, we can confidently refactor the code because if it’s breaking, the tests should let us know.

After using TDD in our project, we find it easier to code in a team especially the integration part. I hope you find it helpful as well in your project. See you in the next post!

References:
Clean Code book by Robert C. Martin
https://www.guru99.com/test-driven-development.html
http://www.nishantverma.com/2010/03/test-case-paths-happy-sad-bad.html

--

--