Do you find Test Driven Development (TDD) difficult to grasp?
You might have tried writing test cases but ended up staring the blank screen for hours trying to come up with the correct test for your piece of code.
Perhaps, you have started thinking that Test Driven Development is not for you. But before you come to the conclusion read this article. I’m sure you would want to give it another try.
Table of Contents
Test Driven Development is not new
TDD is not a new method in modern programming. It has been there from a long-long time. There are articles that says that Kent Beck invented TDD, yet he claims he just rediscovered it.
When Kent Beck asked why is it a rediscovery, he explained:
The original description of TDD was in an ancient book about programming. It said you take the input tape, manually type in the output tape you expect, the program until the actual output tape matches the expected output.
After I’d written the first xUnit framework in Smalltalk I remembered reading this and tried it out. That was the origin of TDD for me. When describing TDD to older programmers, I often hear, “Of course. How else could you program?” Therefore I refer to my role as “rediscovering” TDD.
Mongo bonus points for anyone who can refer me to the original book
Kent Beck
So, according to Kent Beck, the Test Driven Development is pretty obvious to older folks. It comes to them naturally and it should be.
, there is no way to assure that the machine will work as expected until and unless you have tested it. With computer code, these test cases are that assurance.
What is Test Driven Development?
It helps you to design your code from ground up. It forces the developer to think in terms of small units rather than taking on the entire module at once.
TDD helps developers to think in terms of the expected input & output for a individual unit. So, if the output of the program matches with the expectation of the developer, it means the unit is complete.
There are 3 main laws in Test Driven Development:
- Write a failing test case.
- Write just enough code required to pass the test. No code optimization needed at this point.
- Refactor the code and make it optimized. Make sure you don’t wait on it for more than 30 seconds.
Now, that you know what TDD is, let’s move on to see the benefits of TDD and learn why TDD is important for you.
Why TDD?
It makes you write better code.
TDD is a framework that eases the development of the logic. It naturally breaks down a big problem into small manageable chunks that a developer can focus at any give time. This improves the quality of the code by many folds.
With TDD you just cannot start programming at once, you need a vision. I called it a design process because to write a test case for a code unit that does not exist requires planning and imagination of the end product. So, without the vision of the output, you cannot write a test case. And this is the reason many developers simply don’t get TDD as it forces them to test something which do not exists.
Remember the 3 laws stated above. TDD simply revolves around those 3 laws. Following image shows the complete TDD Lifecycle:
TDD provides assurance – The code will work
Just think for a moment and imagine, how would you justify that your code will work no matter what?
Can you prove to me that the piece of code that you have written will work exactly the way it should work without actually implementing it in real?
I cannot think any other way of proving my code quality except writing a test case for it.
If someone questions my code, I have the entire test suites with all the proofs that the code works.
It gives developer that confidence that whatever he has written will work in the production without any side effect, if given the expected inputs. I mean you just know that the code will work.
TDD provides code documentation out of the box
As you write more and more test cases for building a feature, you also build the documentation of your code parallelly.
Most developers (including me) likes to jump directly into the code instead of reading the big documentation written in English :p. TDD provides me with a detailed view of the given component and how that component works.
I can simply open the test suite and graze through the test cases written for implementing that feature. I can learn so much in so little time.
So, if someone doesn’t get the feature, you can point them towards the test cases and he should get everything. Although, they must know how to read code :p
With TDD, you just write enough code to build a unit
This is another thing I love about Test Driven Development. Your methods never go beyond 7 lines of code. And if they do, it means you have combined two or more scenarios together. Try breaking down even further.
You get real time code quality check at every point.
You break your code into multiple pieces, so at any point, you encounter a bug, the respective test case should fail. Immediately, you will know where to look at and what to look for.
Precise coding is a developer’s quality and TDD encourages you to achieve that.
TDD also helps you to avoid over-optimization. You write short and sweet test cases. And once your test pass, you stop writing code and move on to write another test case. That helps you to keep moving forward and not getting stuck looking for a way to optimize the 3-7 liners code.
You see, optimizing 7 liners code is much-much easier than optimizing 3000 lines of code.
You write code once and never look back again
That is true.
Once you follow TDD approach of writing code, then you only write your code once and never revisit it (unless you have to add a new feature).
You know that the code you have written is optimized, precise and free of bugs. You just enjoy your life and not worry about your code at all.
TDD Results in a better team-work
In case you have gone on a vaccation and a new team member has to pick up your work, he can do it very quickly and very easily.
He will open up the test suite that you have prepared, read 3-4 tests and understand the logic behind the creation of particular piece of code and hence start working on it.
In case the test cases were not there, he would have to analyze the entire code method by method to understand the logic and module interaction. All of this takes time and that is why Test Driven Development is highly recommended.
Glimpse of Test Driven Development
There are only two requirements before you start designing your test cases:
- You must understand the problem statement clearly.
- You must have a clear picture of the end product.
So, with the above two points in mind, let’s create a Password Validator.
Problem Statement: Create a Password Validator that would take any string and perform length check.
– Length should not be smaller than 3 and greater than 5
This is a very simple problem statement to implement but let’s do it by TDD.
Step 1: Setup Required Test Data
The setup part is something that should run before every test case. It could be achieved by annotating the method with @Before
.
From the problem statement it is pretty clear that we need a PasswordValidator
class. So, let’s do it.
class PasswordValidatorTest {
PasswordValidator passwordValidator;
@Before
public void setUp()
{
passwordValidator = new PasswordValidator();
}
}
Currently, it will show an error because you do not have the PasswordValidator
class yet. And this is the fun part, you wouldn’t have to create that class by yourself. Your IDE will help you to create it for you.
Hover over the rid jiggly line and select Make a new class
and give the path of the main folder where you would want to have your actual code.
Now, when you run any test case, you will be sure that the Password Validator
class will be initialized.
Step 2: Identify the Test Cases
Test Case #1
In the problem statement, it was explicitly asked that:
- Length should not be smaller than 3 and greater than 5
In TDD, you will break this requirement into its very small part and code that first.
So here the password length should be greater than 3.
Cool, let’s write a test case for it.
@Test
public void itShouldReturnTrueIfThePasswordLengthIsGreaterThan3Characters()
{
String testString = "abcdefgh";
boolean flag = passwordValidator.isValid(testString);
Assert.assertTrue(flag);
}
As we have written the test case, we will be writing the minimal amount of code to test it.
Again, you would see an error in the test case because the PasswordValidator
class do not have the isLengthValid
method yet. So, let IDE create the method for you and then you will insert just the logic into it.
Ok, so now we have an isLengthValid
created for us by the IDE that returns false
as a default value.
public boolean isValid(String password)
{
return false;
}
Let’s run test case and see if it pass.
The test case will fail because we assert the return value to be true
but it gave us false.
At this point, you know that the code is wrong. Now, let’s write enough code to make the test case pass.
public boolean isValid(String password)
{
if (password.length() > 3)
return true;
return false;
}
Now, run the test case again. The test case will turn green and it means the code is working fine.
Note: You can write multiple assert statements to ensure that the code works fine in any given condition.
Test Case #2
Let’s move to the next test case to check if the length is greater than 5 or not. If greater than 5 it should return false else it should return true.
@Test
public void itShouldReturnFalseIfthePasswordLengthIsGreaterThan5()
{
String testString = "abcdefgh";
boolean flag = passwordValidator.isValid(testString);
assertFalse(flag);
}
Now, let’s run the test case to see if it pass or fail.
The test case failed.
Now, just write enough code to pass the test case.
public boolean isValid(String password)
{
if (password.length() > 3)
return true;
if (password.length() > 5)
return false;
return false;
}
The test case will pass, but as you look at your function you will find that it returns false twice. So, can we make it more optimized and clean while still passing all the test cases.
How about if we take the second condition and combine it with the first?
public boolean isValid(String password)
{
if (password.length() > 3 && password.length() < 5)
return true;
return false;
}
Run both the test cases again and see if it pass or not.
Yeah… it passed.
Now let’s revisit the code one last time and see if there is a scope for improvement.
Yes there is… you have called length() method on password twice. We can only call it once and still have the same result. So, let’s quickly do that.
public boolean isValid(String password)
{
String length = password.length();
if (length > 3 && length < 5)
return true;
return false;
}
Cool, you have a highly optimized code block and you have the proof as well that it works. What else a developer need 😀
Conclusion
Test Driven Development is the need of the hour. As more and more competition takes on the market, industries cannot effort to spend more time in debugging.
Everyone wants a working product that is defect free. The TDD allows you to do that. Just remember, TDD is a Design Process.
I hope you learned something of value from this article. And if you truly did, then do not forget to share it in your circle. It means a lot.
For more articles on coding and architecture, refer below link: