Here at Mobiquity, we appreciate our engineering culture a lot. We have fun together and we grow together. One of our cultural components is the quality achieved by the automated testing. Practicing automated testing helped us develop skills and gain knowledge which we now want to share with all the inhabitants of the planet - Earth.
At our last company meeting we were honored to have uncle Bob as a guest. One of our colleagues asked him “what would you regard as a one practice to adopt or mindset to change in order to be able to handle our uncertain future?” The answer was TDD.
At Mobiquity, we love TDD. Among all other benefits, TDD highlights the code duplication. Sometimes, it’s tempting to take a shortcut and copy-paste the code. When you realize you also need to copy-paste the tests, you have quite an annoying feeling. This pushes you to remove the duplication and to feel the satisfaction from the quality code.
Many experienced developers have already tried TDD several times and they still don’t do it regularly. This may have a ton of reasons: old habits, lack of knowledge or even time constraints. Even if the time constraints are off, you still might feel anxious about finishing the task faster. This is just an old habit that drives you away from TDD.
While lack of knowledge is kind of an easy fix, the old habit can be a beast. Mobiquity helps the developers to beat this beast! All around you, you hear people practice and praise TDD. This type of environment builds a positive attitude towards TDD. Project management ensures time for you to deal with the learning curve, if you need it. When you are working in pairs you might be seated with a TDD master which will help you gain this battlefield. If you are working for Mobiquity, you will do TDD, have no doubt about it.
So, if you are finally ready to give TDD one more chance, we have a built-in algorithm to help you practice it.
Understand how to reach the desired result
Make a clear roadmap on how you are going to implement the functionality. Sometimes we don’t have the full knowledge of the environment we use. It is very challenging to practice TDD when you don’t have enough knowledge of a project, framework or libraries. It is a perfect time to make a research on proven practises, well-known approaches and framework capabilities suitable for your task. One of the biggest disappointments from the TDD happens when you realize the code you wrote does not suit the project and you need to delete it all together with the tests you made for it. Taking the time to build a proper roadmap will most likely save you time. You may consider writing comments, pseudo code or just imagine a solution in your mind.
Write failing test(s)
Do it only if you have a roadmap. During your learning path to master TDD, it is better to write just one small test. It feels slow, I know, but it will help you to turn your mindset and get used to the test-first approach.
Once you feel comfortable with TDD, it is acceptable to write a few small failing tests before writing the code.
Write code to make the test work
You are allowed to write the code only when the test to support this code fails. There is a hidden challenge here. When you get back to the familiar approach of writing the code, you would be tempted to write more code than the existing tests cover. Stop yourself there (or stop the driver if you are the navigator in the pair programming session). Just stop. Write the code only to support failing tests and stop. Stop!
Write the next test or make a research
It’s time to take the next step when you are ready to write the next test. Now, it’s time to do it! It could happen once in a while that the library behaves a bit differently from what you expected or what documentation provides you with. Or you can come up with an idea on how to optimize the code. In that case, just go back to the first step and do the research in order for you to continue.
When we do not do the TDD
Except for the Friday evenings, which are dedicated to socialization, we have one reason to distract us from TDD. TDD is the key to a good quality. You need it for any production software. On the other hand, sometimes you need to do PoC or MVP. Such projects do not always go to the production stage and the required quality bar is low. While time is the key, it makes perfect sense to skip TDD and just do a quick and not reliable prototype for these kinds of projects.
The Arrange-Act-Assert or three A convention became almost a standard across the industry. You might have also heard about the `Given When Then` convention. It is essentially the same, but in different words. This convention helps you to make tests easy to read and cheaper to maintain. AAA convention suggests dividing the test into three sections. Each section has one responsibility. The section name clearly describes its goal.
The Arrange section contains only the setup for the test. In this section, objects are created, mocks and spies are set up (if you are using any). Then there is the Act, which is the invocation of the method being tested and possibly waiting for it to finish all the triggered async operations. Assert is to simply check if the expectations were met.
It is perfectly normal to have an Arrange and/or Assert parts shared between several tests.
I suppose everyone had such an experience - after the hard working test preparation and the invocation of a method under the test, you finally have a working test. Now it’s time to do the next test with the same preparation and check the second thing that should happen. It’s so tempting to use the shortcut and add one more assertion into the existing test.
Let's consider this test method.
You clearly see 3 assertions here - 3 calls of the jasmine’s “expect” method. Let’s say the test fails with a message “Expected '!' to be 'Great tip!'”.
My first question for this would be “what does the text comparison have to do with the tooltip position?” It’s impossible to understand the problem without exploring the test and the component code. That is one of the big issues of placing several assertions into one test - it’s very hard to come up with a proper test description so that the test title would help you to spot the problem right away. We use the rule of thumb - one assertion per one test. The code above could be easily refactored by grouping the tests and using the common Arrange and Act parts inside beforeEach.
The decision on what to test and what to not test has some extent of art in it. Sometimes the borderline is not clear. The reusable library needs more coverage than a pet project.
We find the practice of testing only the end result of the subject under the test to be the proven way to easily support tests.
Let’s say we have a method showing the alert. The method accepts the parameter, manipulates it and shows some text in the alert.
It’s obvious to test the alert call. You might have an idea of having a test checking if “Utils.getAlertText” method was executed. Later on, the next developer might realize the logic of “Utils.getAlertText” can never be reused as it’s quite simple. It would be wise to inline it instead of placing into the Utils. Inlining means to break the test and check the “Utils.getAlertText” call. While the app works exactly the same, the test is failing. The practice to test only the end result helps you to avoid such issues.
I hope these proven practices inspire you to invest time into developing and exercising unit test related skills. It will pay off in a manner of a good reputation, bring you satisfied clients, and more time for creation instead of maintenance
Don’t be afraid to write tests. Start today! And start changing your mindset towards TDD. Enjoy the coding!
We believe that addressing customer challenges gives you opportunities to delight. Using our proprietary Friction Reports and strong industry expertise, we dig deep into customer sentiment and create action plans that remove engagement roadblocks. The end result is seamless, relevant experiences that your customers will love.