What’s wrong with TDD

No comments

Monday, May 26, 2014

A while  ago I was asked to talk about the problems of using TDD – being me I’ve decided to do the exact opposite, this session was names “what is wrong with TDD”.
I felt that one of the major issues is that TDD looks weird, it’s counter-intuitive, and convincing developers to actually try it hard and requires a mental leap of faith.
And so I’ve created the talk aiming to help those developers who wish to teach their team a new methodology (because TDD is a tool not a religion) and need “ammunition”, answers for their co-worker’s questions, suspicions, rants and excuses for not using Test Driven Development.image
Since then I got to present this talk several times both as 15 minutes  lightning talk as well as a 2 hours session (and anything in between) –  and so I thought to write a blog post that would summarize these sessions on the topic of reasons of not using TDD and why they are wrong…

I don’t like tests/I don’t have enough time

I believe TDD and “unit tests” has been done a great injustice by not giving it a cooler name – preferable one that doesn’t have the word “test” in it – because it’s a PR disaster!
A few years ago I have been invited to talk about “TDD in the real world” at a local user group – around the half hour mark one guy who set at the front row raised his hand and asked me – “does that mean that developers write these tests?”
It’s easy to understand why some developers think that “tests” are not their job – after all they are called “developers” and not “testers”.
Usually this lack of enthusiasm of writing these strange little tests hides behind a more “acceptable” argument – I really want to write unit tests (wink, wink) but unfortunately I do not have enough time to do so – obviously such developers are developing the only project in the world that have a deadline.
There is truth in the “I don’t have enough time” argument – writing code with unit tests takes more time then writing code without, I call this development phase as the “hitting keyboard phase” and it could take from 15% to 35% (!) more time – even more on hard to test projects.
But the benefits of TDD are much greater and in fact using TDD we reduce the time it takes to get your code to the client (the one who uses your code) by having less bugs (around 40% to 91% less bugs) and more maintainable code. I wrote about it back then when I worked at Typemock  - The Cost of Test Driven Development.
image
[Realizing quality improvement through test driven development: results and experiences of four industrial teams]

In conclusion – Writing tests costs time, but overall development takes less time. It’s an investment and like all investments you can choose not to make it – it’s up to you. Just make sure you’re aware of the cost of that decision.

Writing tests before the code is counterintuitive

Since most developers cannot predict the future – how can you know which tests to write before actually writing the code?
I think there are two real reasons not to want to write tests before the code:
It requires leaving you comfort zone
Up until this point you were taught to think of all of the possible scenarios and find the optimal solution, using the power of design methodologies learnt during a long and successful career. And now you need to “not think about design” and develop one test at a time – an it’s suppose to fail!
Planning for failure
Deep down it’s hard to see something I wrote break – even if it’s just for a little moment. And so writing a failing test a.k.a the first step of TDD is hard for some.
What would a developer not willing to try (exit comfort zone) do? Write unit tests – after writing the code and try to sell this practice as TDD.
This is usually a bad idea – most experienced TDD practitioners can tell whether or not the unit tests has been written before or after the code. And writing unit tests for existing code is harder, much harder than writing the tests before.
A developer who write unit tests after writing his code is missing the whole point – TDD is a design methodology – the unit tests are just a by-product of the process.
image
It’s about growing your code writing only what you need and about emergent design and not about writing unit tests.

Not everything is testable

This is actually a good reason – when this is really the case.
There are pieces of code that cannot be run as part of unit tests. I’m not talking about bad written code – there are ways to handle these. I’m talking about UI, video streaming, and some closed architectures that make unit testing almost impossible.
Needless to say if you cannot write unit tests – you cannot use TDD (duh).
But make damn sure that this is the case – because hard to test is not untestable:
  • By following MV* patterns (MVP, MVC, MVVM etc.) you can still test the UI business logic and drive its design using TDD.
  • Mocking frameworks are there to help you stub/mock external dependencies (Web, DB, 3rd party components)
  • When all else fails write integration tests (yes – as part of TDD)
In fact there are many ways to test this so-called “untestable code”, books have been written about it and there’s a plethora of tools to help with unit testing less than trivial code.
In the end you might not have 100% test coverage (whatever that means) but at least you’ll be able to easily maintain and develop your business logic.

TDD locks design

I get this usually half way through teaching a unit testing/TDD course - what would happen if we write all these tests and then need to change the design?
It feels like a big waste of time re-writing tests over and over again just because code (being code) was changed to fix a bug or add functionality.
Once I even got a complaint by a co-worker that “writing the code took one hour but fixing the tests took half a day”.
The good news is that there are principles of good test design that would prevent having to change your tests every single code change – this is where one assert per test and don’t test private methods come from (and there are many others).
Ideally only requirement change or a bug should cause your tests to break. This is not always the case but after writing a few hundred tests, reading a good book (or blog) you’ll find what to avoid and how to reduce the cost of maintaining your unit tests.
image
Good tests are simple, robust and easy to update if the need arise – bad tests get deleted and replaced.

Conclusion

TDD is not perfect:
  • It requires time investment
  • Seems counterintuitive at times
  • A new skill to learn
  • It might not work for all scenarios
But none of the reasons above is a good reason not to give it a try – it’s a powerful methodology that helped me combat “analysis paralysis”, create robust, maintainable code and there’s the added benefit of the resulting unit tests which provide a safety-net against regression bugs.
image
So why don’t you give it a try?

Things I learnt reading C# specifications (2)

No comments

Monday, May 12, 2014


The story so far: After reading Jon Skeet’s excellent C# in Depth - again I’ve decide to try and actually read the C# language specification
You can read about in a previous post of mine:
Things I learnt reading C# specifications (#1)
And so as I continue to read the C# specifications along with the excellent comments by many industry leaders (I own the annotated version) and I keep discovering cool stuff about the language I’ve been using for more then a decade.
And so here is the second list of things I found out while reading the C# specifications:

args can never be null

Any .NET developer who ever created a console application knows what I’m talking about – there’s always a Main method with one of the four possible signatures:
  • static void Main()
  • static void Main(string[] args)
  • static int Main()
  • static int Main(string[] args)
Two of which pass the command line arguments (if any) used.
I’ve always was a bit defensive about the args maybe due to scars I have from writing C++ programs - although in C++ args would never be NULL but that’s a different story.
In any case according to the C# specs:
The string[] argument is never null, but may have length of zero if no command-line arguments were specified
So there you have it – one less thing to check for.

C# uses banker’s rounding

Every needed a value rounded in .NET – it might happen if you use the decimal type or explicitly using Math.Round. Ever wondered what would be the result of such round?
The fact is that usually it would behave as expected i.e. round to the closest number (integer) which means 0.9 becomes 1 while 4.2 becomes 4. But what happens when rounding half a number would it round or down?
The answer is “it depends” -  in fact both 42.5 and 41.5 would be rounded to 42!
This rounding happens mostly due to the fact that it’s the answer for the ultimate question but also because the rounding method used rounds to the closest even number. Known as the Banker’s rounding and it’s has many nice properties:
Over the course of many roundings performed, you will average out that all .5's end up rounding equally up and down. This gives better estimations of actual results if you are for instance, adding a bunch of rounded numbers. I would say that even though it isn't what some may expect, it's probably the more correct thing to do.
[From StackOverflow]

Boxing and “is” operator

All us .NET developers knows about Boxing & Unboxing, those of us who used the early 1.1 version used them extensively when using collections (we were young and needed the money and besides generics were not implemented yet).
Luckily these days I don’t use boxing/unboxing much but from time to time a value type (e.c. int) needs to be passed as a reference type and Boxing occurs.
But is it still a value type – of course not! it’s a reference wrapping a copy of the value we’ve been using so how come the following code would write “True”?
int myInt = 42;
object aBox = (object) myInt;

Console.WriteLine(aBox is int);

Why’s that? well, it’s part of the specs (4.3) and besides it’s the way I’ve subconsciously expected it to work in fact I’ve been using this behavior without noticing for years -  I had a int and so please don’t confuse me with Boxing because in my eyes it’s still an int.

That’s it for now, I’ll keep reading the specs and probably produce a few more posts on the way when I find interesting facts about the language I’ve been using for more then a decade.

Until then – happy coding…
Related Posts Plugin for WordPress, Blogger...