SyntaxHighlighter

Showing posts with label TDD. Show all posts

On learning practical TDD

If I had to sum up my last two years of experience as a developer, I would say using dependency injection to write code that can be easily tested would be the most important thing I've learned. And the single most important site that kicked it off for me would be the one from Misko Hevery, where he teaches how to write code that is easily testable. (If you haven't read all his articles and seen all his videos, please do so. It will change the way you look at code.)


The next logical step is to adopt Test Driven Development, or Test Driven Design. This is about letting the test cases drive the design of your software. The benefits of TDD are well documented, it's been proclaimed as the best way to build software. (Well, it isn't appropriate for all kinds of software, like implementing algorithms. Refer wikipedia for criticism of TDD.)

It is widely believed that the best way to learn TDD, is to pair with someone who already does it. But where does that leave people in non-agile companies? The whole point of this post is to share my experience on how I'm going about learning this agile technique.

Step 1 would be to go through the classic by Kent Beck, "Test Driven Development: By Example". This book teaches the absolute basic techniques that help a developer think in TDD terms. Kent goes step by step, refactoring at each step, even sharing with us his thoughts on why he thinks a particular refactoring is required. Please note that knowledge of refactoring techniques is another requirement for TDD. To put it simply, refactoring is the disciplined process of removing duplication from your code, and improving its structure.

But the problems that Kent solves in his book, is in the context of a maintenance project. If you were to start with a new project, trying to apply TDD, this wouldn't be enough, since, it seems that Kent is working with a project that is already covered with a test case harness.

Another book, Growing Object-Oriented Software, Guided by Tests, by Steve Freeman and Nat Pryce, takes us further and applies TDD in the real world. I'm still going through this book, but its already helping.. for instance we learn that a real TDD process begins with an acceptance test. This makes sense as it forces us to set up the infrastructure in the first iteration of the project, surprises if any will be encountered early on, giving more time to tackle them. More on this when I'm done with this book.

The book by Jimmy Nilsson, Applying Domain Driven Design And Patterns, looks at these techniques from the .NET perspective. The fun part is where he tries to mix up DDD with TDD. The way he goes about his sample problem, tells us that there is a lot of to and fro when using TDD. We take a step, another, and see a problem or sense a code smell, and then we start backing up from the steps we just took. Also, he, kind of, blasts the notion that there is no upfront design when applying TDD. There is some design, but we don't spend too much time thinking about it, so it isn't the big upfront design that was seen in the classic waterfall. The point is not to invest too much time in the design because surprises are inevitable.

For those who like live action, Brett Schuchert from Object Mentor, has posted videos that demonstrate TDD in both java and C#.

Learning the difference between mock objects and stubs, and practicing Mocking techniques with at least one good mocking framework ( The current favorite that is getting coverage in many blogs is Mockito but it still doesn't have a lot many users, the previous one was Easymock, and has better documentation and will be easier to use since many books show how to use it).

As of now, I'm hitting the stage where I can write some tests and the code, but the tests seem to be a burden because every time the code changes, making the tests confirm to the changed code become a nightmare. I know that brittle tests is a big problem, test code should actually be the easy part, and it probably implies that the code is not easy to use and has smells... the warning here is : don't go applying the test first approach in the workplace right away... spend some time practicing TDD before applying it at the place where you work, because it can take a lot of time, everytime you are stuck, it will be hard to get help in a non agile firm.

On testable code and TDD

Ok, so I haven't blogged in a while...
But I think I've learnt a few things over this period. For a while, I think I was enamoured with writing testable code, and I took it too far. So much so that today when I look at my code, there are mostly dependencies being injected through the constructor everywhere.
Well, isn't that a good thing, you might ask?
Not necessarily. Such code tends to get defined by its static struture if you approach all your development this way. Unless we look at behavior more than overall structure, we will not get the right abstractions in place. When designing classes, the question to be asked is this: which classes does this class need to talk to?
Identifying abstractions followed by collaborations seems to be the most dificult part when designing OO software.

Another idea that I have been trying to learn is that of TDD.
Writing tests is not enough. Till now, all my efforts were towards refactoring existing code, to make it more flexible. By injecting dependencies and defining test versions of the same classes, I thought I would be able to crack the design issue everywhere, and I was way off target.
Again, I was not exactly on the mark when injecting dependencies either. Till now I had no idea of the mocking frameworks that people use to ease their unit testing efforts. I need to look into the differences between state verification and behaviour verification.
Apparently, the right way to go about writing code is in the exact reverse of what I have been doing till now : you write your tests first, and then the code follows. The rationale behind this approach called TDD, is that if the tests are written first, you only write enough code to makes your tests pass, thus staying away from the "big design upfront", which may or may not materialise, and more importantly which will perhaps not be required. The test code will be considered your specification. If the client asks for a feature, then you must have a test for it. Looking at in reverse, everytime you get a bug reported, step one must be to write a test that can reproduce the bug. Only then do you work to fix it.
Tests are also considered as doumentation. Some TDD practitioners will even refuse to look at your documentation, the first thing they look at is your test suite. Their logic: documents may lie because developers don't often keep them up to date, tests are the real deal.
They follow the RED-GREEN-REFACTOR cycle. Write a failing test case, write enough to make that test case pass. And then refactor like crazy. The aim of refactoring must be removal of duplication. And trust me, Kent Beck's book (Test Driven Development by Example) will surprise you on the topic of identifying duplication when taking small steps.

I still don't understand what they call "interface discovery", this is what makes TDD an approach to design, and not just a process that ensures your test cases get written. Another area that is fuzzy to me: how much of design is required before you can start TDD? Do you approach the screen with a blank mind, taking it from there? Or do you think a few things out before you start tapping away?

Powered by Blogger.