If you are new to TDD, and interested to start practising it... this blog is intended to guide you in the journey!!
So, lets start with,
What is TDD ?
TDD - Tests that Drive Development, an agile methodology for writing tests.
The eXtreme programming practice advocates writing Test before Code!
Lets see 10 quick things to understand it better!
>> Mind is the ultimate weapon!!
The first and must pre-requisite to start TDDing is to tweak your developer's mindset, to start with the tests!!
As a developer, It's very much common to jump into development. But you should change this mindset...
Don't code unless you have a test for that!
>> 100% Test coverage doesn't ensure TDD
Say, your code coverage tool says 100% coverage... that doesn't been TDD is followed. You can write tests even after coding but that is never TDD and you will not get the benefits of TDD!
Although the reverse is true, when you follow TDD you will end up with ~100% test coverage.
>> Red Green Refactor!!
Heart / brain / backbone - call it however you want!
This is the core of TDD!
Red -> Write a test that fails
Green -> Write minimal code to pass that test
Refractor -> Refactor your code!! (Don’t worry the test is there to shout if you change the behaviour)
And then, start from Red - writing another test...
By Following this cycle, you end up writing your code using TDD!
>> TDD rules!
Thanks to Uncle bob's three TDD rules, you are clearly warned about the Don'ts of TDD!
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
>> Later is hidden "never"
Procrastination is a sugar coated version of that's never gonna happen 😛
The cost for doing this with your tests is high!
Say, you tweak your code as last piece for the upcoming prod release!
You see one of the test cases failing - while digging deeper, you find that the test needs to modified to make it pass but the code is fine! - You take the timeline excuse to SKIP THE TEST --- So this on a longer run is going to disturb your dream Quality product despite following TDD!!
>> Attributes of Test
As seen previously, there can be cases where your tests are faulty!
By following few guidelines, you can ensure your tests cases are written in a correct manner.
Write SLIM tests!
S -> Single concerned
Ensure your tests are not asserting or checking too many things! SRP applies to tests as well. Your test should pass or fail for single piece of functionality.
L -> Logic agnostic
Tests should be logic / implementation agnostic! They should just bother about actual and expected results and never on the logic to form the expected results.
I -> Independent
There shouldn't be any order in which you can run the tests to make them pass! All of them should be independent of each other. Don't use this order feature of the testing frameworks!
M -> Mock the rest!
Apart from the System Under Test, mock all the other systems!
Say you are testing a class, all the other classes injected into SUT should be mocked. You can make use of various mocking frameworks available. If not, by using actual implementation of other systems, you end up testing other systems to - which is out of boundary!
>> Well, TDD != Unit tests
TDD talks a lot about unit testing. But TDD can or is also about upper test layers of the testing pyramid!
TDD is just an approach, be it integration or acceptance tests - these can also be done using TDD!
For example, the acceptance tests are like user stories, these Given-When-Then tests can be written well ahead of the development of the feature itself! From a big picture, these can be used to ensure if all the expectations are met after the feature is developed... on a longer run when the system is refactored at a wide scale!
>> Derive your test cases!!
Your test cases define the success of TDD!
Writing test before code is mandate... but how to come up with minimal test cases?
You should write one test case asserting one expected behaviour and that should fail for the current implementation...
-> Fake it, till you make it!!
Run your base test, by faking the source implementation.
Say you test for Add(2,3), hard code / fake the source implementation to just return 5!
Keep adding more test sets to break the current implementation... Think and add the below test cases,
- exceptions / edge cases
- degenerative cases
- Ancillary cases As the cycle, repeats... you will find a place where you can generalise the implementation!
-> Finally, make it Obvious
When you are able to generalise the approach, code out the obvious code you envisioned!
And... You are done!! You have your test cases to validate your obvious code!
>> Be happy to see your test fail!
Red need not attribute to Danger🚨 always...
Get a smile when you see a RED with TDD!
It's important to see a failing test case, before writing your production code... (No, I am not being superstitious here! 😅)
It's important to check if the test is failing, and failing for the right reason! Else, you might think the code you wrote is satisfying the test. This false feedback will either result you with useless code or hidden bug!
>> It's an Iterative process
I know all possible inputs,
I will write all test cases at once!
implement the logic at once!
And feel better as I followed TDD!!
TDD is an iterative approach! Getting all your tests suits before hand doesn’t ensure you followed TDD...
TDD helps you to derive these tests and write minimal or necessary code! The process guarantees that,
=> Any code you write is tested by a test case!
=> Any test you write is green because of a piece of code!
Thus, boosting the confidence on your code! and to be more aggressive with your refactoring skills!!
Those are the ten things I wanted to share about TDD.
Hope this helps you to kick start your journey with TDD!