What is Test Driven Development made for? Is it for requirements? Design? or Code?
First, TDD conveys the message – “You know it well, before you code it”. It basically encourages that the Quality is built into our software proactively by “development through testing” than reactively by “testing after development”. So, we use the testcases for constructing rather than validating.
Next, the questions that may arise are – “How would I implement TDD cycle within a given iteration or sprint?”.
---Should I implement a Waterfall cycle within the iteration (Get all testcases ready first, then design, code and test)?
---OR should I write the code test-by-test basis, and progressively refactor the code so that the design evolves through refactoring? (Lean within lean development)
Oops, the seminar which I attended on Agile didn’t cover this? :(
It is always easy to suggest one approach or the another, but before we come to any conclusion, let us do a small exercise on TDD.
Let us say our current iteration is “implementing elevator(Lift)” for an on-going software project. Don’t ask me 'what is that project about?'. :). I’ve picked up this example as I thought it would be better to convey the concept through a real world example which almost all of us see and use day-in and day-out.
Our Architect is now kick-starting with writing the testcases with a brainstorming session. Let our Mr. Architect speak it out in his own words…
What are the different requests and how they need to be processed?
(External Request) Elevator has to go to a floor if there is User request from that floor.
----Elevator has to move UP if the request is from an upper floor.
----Elevator has to move DOWN if the request is from a lower floor.
(Internal Request) Elevator has to go to a floor if there is User(inside elevator) request to go to that floor.
----Elevator has to move UP if the request is to an upper floor.
----Elevator has to move DOWN if the request is to a lower floor.
When does the elevator stop?
----Elevator has to STOP at a floor if there is an external request or internal request to stop at that floor?
What happens when the elevator stops at a floor?
---- Open the Door
---- Update the internal request as DONE or DISCARD.
---- Allow time for passenger to Get-In and Get-Out
---- Handle max-load checking, if any
---- Close the door
---- Update the external request as DONE or DISCARD.
---- (Well, I am not writing all other features (light, fan, displays etc.) of our modern elevators as we are not really going to write the software here)
...(Okay, my requirements are evolving well. TDD is proving good, thanks to my Agile coach.)...
Then, one junior engineer in my team has brought up a question:
“What if the Elevator is going down and there is a request from a floor(on its route) to go up? Should the elevator stop to board the User on its way-down or should it stop on its way-up?”.
(This junior has joined the industry hardly an year back and doesn’t carry a “Software Architect” title as I do, but has brought up very good design point. Does it what “Wisdom of the Group” mean?)
Well, the answer is “Let’s check with the customer on what he wants but there is a need to have a mechanism to decide whether to stop or not on the way and we would take this as a Black Box (a decision-making method) and it would be handy from code maintenance point of view too even if the requirement changes in future”.
“What if there are requests from upper floor and lower floors at the same? How do we decide the direction the elevator has to move?”
I seriously envy she has asked this question but I have started building up a few more testcases on top of what she has asked and 'it is only getting better'…
- ----If both of them are to move DOWN
- ----If one of them is to move UP and one of them is to move DOWN
- ----Is there a Least Travel requirement to be handled?…
Well, this has also ended up as a TODO for us but we have added very good set of testcases coming up in the form of “executable specifications”, and what do good “executable specifications” do? They feed to the design and that’s what has happened now – we need another Black Box((a decision-making method) to decide whether to move up or move down. Also, we have identified the parameters(variables) that these decision-making methods need.
Along with this, we have identified different actions (Just execute them – moveUp, moveDown, Open, Close). Also, we have, almost, identified the Data Structures to store the User Requests (Internal and External). Overall, our skeleton code is pretty much visible now.
Of course, there are still a few questions to be answered but they are not impediments in that we still have the job to do for the next few days before the questions are answered. We don’t need to blame someone that we are awaiting answers to start coding. We have “blanks” (TODO) to fill in our design and we knew where the blanks exactly are.
Now, let’s get back to those questions that we had earlier:
---Should we implement a Waterfall cycle within the iteration?
----Even if the scope is an iteration, practically we cannot get all the testcases right or wait to get them right to start with your design and code. First let us organize our testcases properly and design would be discovered automatically.
----Thus, it also helps proper work distribution if multiple people are involved in the implementation.
----Random organizaion of testcases wouldn't help a good design and proper work distribution.
---OR should we write the code test-by-test basis and progressively refactor the code so that the design evolves through refactoring?
----Looks like, one-size-does-not-fit-all. It may still work, but there would be a lot of code-refactoring, test-and-retest cycles and they consume a lot of time.
----Also, remember that Automated Unit tests are not possible for all scenarios of software development and let us not ignore this fact.
----Refactoring doesn’t just mean abstracting the reusable code and thus evolving the design with a test-by-test approach. Let us not make it too lean in the name of lean development.
----If a few minutes of 'collective thinking' could save hours together at later point of time, then do that NOW.
So, let us go with the facts (than what was taught) and use a right mix of waterfall and test-by-test models as per the context.
Note: If you observe the elevator functionality derived from these testcases, it can be easily seen that the Elevator works like a State Machine (OOAD design). But, the objective of this article is not to explain any particular design pattern. Maybe, we would take it some other time.
Are your testcases well-organized for a good TDD implementation?
Are they proven good for an organized task distribution? Don't forget this project management issue.
Is your TDD approach feeding enough for a good design?
Are you ensuring that you are implementing the 'team collaboration' and 'wisdom of the group' in a right way for well-organized testcases?
Do you have an eye for minimizing the rework by spending the right efforts at the right time?
(Attribution: Images on this post are downloaded from FreeDigitalPhotos.Net)