Why Test Driven Development (TDD) is the Best Way for Robust Coding.
First, write unit tests, then write the code.
Some years ago, when I first heard of “Test Driven Development”, I was skeptical.
The very idea of “first write unit tests, then write the code” was gibberish to me.
Why wouldn’t it be? Write your unit tests first? Who would do a goofy thing like that?
But I’d been a professional programmer for 10 years by then, and I’d seen things come and go in the industry. I knew better than to dismiss anything out of hand, especially when developers were so gung-ho about it.
So I consulted a friend of mine who showed me a basic example.
This basic example has a class LCL_SUM with a method SUM. This method’s responsibility is to ADD up the numbers. It takes a number as an importing parameter and then adds it to itself to derive the result. Let us call this method as a production method.
The code for the class was like this:
CLASS lcl_sum DEFINITION.PUBLIC SECTION.METHODS: SUM IMPORTING iv_1 TYPE iRETURNING VALUE(rv_sum) TYPE i.ENDCLASS. “lcl_sum DEFINITION*START-OF-SELECTION.* Nothing here yet**CLASS lcl_sum IMPLEMENTATION.METHOD SUM.rv_sum = iv_1 * iv_1. “Deliberate mistake (I multiply instead of add)ENDMETHOD. “sumENDCLASS. “lcl_sum IMPLEMENTATION.
Test Class setup
Now create a class which acts as a test class. In SAP you would need to add the keyword FOR TESTING when defining the class. This addition separates this class from the production code.
CLASS lcl_test DEFINITION FOR TESTINGPUBLIC SECTION.METHODS: m_sum FOR TESTING.ENDCLASS. “lcl_test DEFINITION*CLASS lcl_test IMPLEMENTATION.METHOD m_sum.ENDMETHOD. “m_sumENDCLASS. “lcl_test IMPLEMENTATION
Test method implementation
In this test method, what you need to do is — Test the production code. So, to be able to test the method SUM of LCL_SUM, you would need to instantiate an object reference to LCL_SUM, call the method SUM sending the dummy value.
Based on the Dummy value, the method would send you the result — the actual result from the method. Based on Dummy value, you know what would be expected value. E.g. If you pass number 3 to SUM method, it would give you the result of 6 as it is adding to 3.
Once you receive the actual result from the production code or method under test, you need to compare the results. If the actual vs expected is not matching, you need to let the system know that something is wrong with the actual vs Expected and show a suitable message.
CLASS lcl_test IMPLEMENTATION.METHOD m_sum.DATA: o_cut TYPE REF TO lcl_sum.DATA: lv_result TYPE i.*CREATE OBJECT o_cut.lv_result = o_cut->sum( 3 ).*cl_aunit_assert=>assert_equals(EXP = 6act = lv_resultmsg = ‘something wrong in output’).ENDMETHOD. “m_sumENDCLASS. “lcl_test IMPLEMENTATION
Unit test Results
This tells me that, there is something wrong in the production method implementation.
Yeah, if you look closely the method implementation of the SUM, I have a typo –instead of using Summation, I had used Multiplication. So, I would correct it and re-run the test to execute it successfully.
I was hooked to TDD. Flabbergasted would be the right word.
What was amazing was the highly reduced cycle time of development and testing.
I was used to writing code for the better part of an hour before trying to compile or run it. But here the code was getting executed and tested every 2 minutes or so.
Thus, in a nutshell, TDD is realized through short development cycles that follow the rule “first write unit tests, then write the code, then refactor, then repeat.” Unit tests are automated tests that check whether functions work as expected. Your very first unit test should fail since it’s written before you even have any codebase.
You add a bit to the test case code. You add a bit to the production code. The two code streams grow simultaneously into complementary components. The tests fit the production code like an antibody fits an antigen.
This measure prevents developers from writing unnecessary code that doesn’t comply with the given test.
And this whole integrated approach offers a litany of benefits to the developer.
You Fix Bad Code Without Breaking Anything.
Whenever you see bad code, you roll your eyes, pray to God and utter one of the two statements.
· “This is a mess. I guess I have to somehow fix it”.
· “I am not touching it.”
Either case, there is an element of fear involved. In fact, uncertainty.
What if my code breaks the existing functionality?
TDD helps you precisely to overcome that uncertainty.
It’s important to point out that in a TDD environment, developers focus on running tests to prevent bugs rather than to remove them after the code is written. This is one of the most powerful benefits of TDD. When you have a suite of tests that you trust, then you lose all fear of making changes. When you see bad code, you simply clean it on the spot.
And the tidier your code, the less effort your team must put into adding new features or modifying the existing codebase.
TDD Enforces Documentation.
Dick Brandon hit it bang on the nail when he observed.
“Documentation is like sex; when it’s good, it’s very, very good, and when it’s bad, it’s better than nothing.”
Documentation is the castor oil of programming. Managers think it is good for programmers and programmers hate it!
one common reason why scope creep occurs is lack of documentation with clearly defined requirements. This problem can be mitigated through test driven development.
In a TDD environment, developers write unit tests to test particular segments of code. Unit tests serve as specifications that describe the precise features that should be implemented. Therefore, well-specified tests prevent developers from writing superfluous code.
And these unit tests are documents. They describe the lowest-level design of the system. They are unambiguous, accurate, written in a language that the audience understands, and are so formal that they execute. They are the best kind of low-level documentation that can exist.
TDD Helps in Better Design
The fundamental prerequisite in TDD is that you have to write your unit test cases before writing the code.
The problem with testing code is that you have to isolate that code. It is often difficult to test a function if that function calls other functions. To write that test you’ve got to figure out some way to decouple the function from all the others. In other words, the need to test first forces you to think about good design.
This creates a better, decoupled design in which you have better control over things as the code develops.
While writing test cases upfront might consume time initially but this brings a lot of benefits. Developers admit that previously they used to write lines of code, realize that their solutions were irrelevant, and then start coding again from scratch.
Unlike outdated coding practices, TDD allows developers to go back to the drawing board and concentrate on designing a lightweight, flexible architecture upfront.
And the very fact of writing test cases upfront prevents any bugs that might pop up later thus saving time, effort and heartburn.
And Lastly TDD Follows Best Coding Practices.
TDD promotes good coding principles including DRY, KISS, YAGNI and SOLID.
The DRY (Don’t Repeat Yourself) principle tells developers to avoid repeating the same code in different parts of the same system, which is why it’s also sometimes called the DIE principle (Duplication Is Evil). DRY recommends that developers use classes and functions to encapsulate system functionality and maintain a consistent codebase.
The KISS (Keep it Simple, Stupid!) principle advises developers not to reinvent the wheel, but to build simple and clear architectures. The essence of KISS is to avoid over-engineered solutions.
The YAGNI (You Ain’t Gonna Need It) principle fights gold plating. Gold plating might seem harmless, especially if a developer is eager to enhance existing functionality to delight a customer. However, it results in extra development time which might cause a project delay or a disgruntled customer. YAGNI makes it clear: a developer should implement only assigned tasks and avoid adding excessive functionality.
SOLID consists of five principles in one: single responsibility, Open-closed, Liskov substitution, interface segregation, and dependency inversion. To be brief, SOLID states that following these principles makes applications easier to maintain and test.
In a nutshell, TDD helps in creating elegant and simple code which is easy to maintain.
As Robert Martin has aptly said.
“Clean code always looks like it was written by someone who cares.”
Extreme Programming: Kent Beck.
Agile Software Development: Robert Martin
Refactoring: Martin Fowler
About the author-:
Ravi Rajan is a global IT program manager based out of Mumbai, India. He is also an avid blogger, Haiku poetry writer, archaeology enthusiast, and history maniac. Connect with Ravi on LinkedIn, Medium and Twitter.