A Rant About Unit Testing and Stuff

I think it is time for another rant. Actually I really don’t, as my blog is supposed to be about coding, not about me ranting… It seems like my blog has slowly gone from code centric posts, to ramblings of a grumpy Microsoft developer. Sorry about that, and I promise to focus more on code in the future…

Lately I have travelled around and talked at a bunch of conferences, and heard a lot of really smart people talk about a lot of really cool things. This got me thinking a bit about my own way of dealing with new technology, as well as about my own way of doing things today… The result of this thinking is that I am going to try and make 2 changes. First, I am going to try and get out and about and try more new stuff. More frameworks, technologies and maybe even more languages… Hopefully this will fuel my blog with more code related posts as well as interesting revelations and thoughts. Secondly, I might have revisit my stance on unit testing and TDD…

And by now, people who don’t know me personally go “but Chris, everyone has to unit test….you aren’t unit testing?”. And the answer to that is “no, I don’t”. Why? And the reason for this is actually quite simple. I started working as a trainer 5 years ago, and at that point, unit testing wasn’t nearly as big as it is today. And working as a trainer, unit testing doesn’t really come into play.

I kept working as a trainer for just over 2 years before moving to New Zealand to work as an EPiServer developer. And once again I ended up in a world where testing wasn’t really feasible. At that point in time, mocking out EPiServer was just to hard. And trying to change the way the company was doing EPiServer development would have meant some serious work and lots of non-billable hours, which the company would not allow. So testing remained off-limits.

After a while, I moved from doing EPiServer development, into building solutions that were generally slightly different than “normal” development. It was much closer to POCs and spikes in a lot of cases. My work included demo solutions for Microsoft as well as really odd other projects. Getting started with thorough testing was once again not feasible due to the way that the projects bobbed and weaved through changes, as well as ridiculously short deadlines…

As an example I was tasked to build a kiosk application using Silverlight, WPF, WCF, Azure, ActiveX, WindForms and more, and integrating with a custom printer, touch screen, a credit card device. And I am not talking a bunch of different apps in these different technologies. Most of them were in the same application. And I got 3 weeks to build it and get it into production. A task that most developers would probably prefer not doing.

It was an application that would probably have benefited a whole heap from automated testing, but with a 3 week deadline set in stone, I just dug in and tried to get the application out on time…

So now that I am back in Sweden and working for a company with heavy focus on automated testing, TDD and CI, why am I not testing? Well, the answer is just as simple as it is childish to be honest. I don’t like the way that devs in my vicinity are arguing the benefits of unit testing. And when people argue stuff using stupid arguments, not to mention more or less forcing me to do things in a new way due to arguments I don’t agree with, I take the childish way out and start argue the absolute opposite. Childish? Absolutely, but that is just the way I work…

However, having been around other more or less brilliant developers a lot of time in the last 2 months, I have heard enough good arguments to make me consider unit testing. And funnily enough, most of them agree with me when it comes to the arguments being thrown at me from the other devs.

There are a couple reason that I listen more to the devs I meet at the conferences. Number one, they travel the world speaking about technology (aka they know what they are doing). Number two, they are good at arguing their points using good arguments. And they are often pragmatic enough to add “but it depends on the situation”. And lastly, it might also be because when you have a couple of beers with these guys, they talk about new technologies, interesting frameworks and good usage of patterns, mixed with a whole heap of really funny jokes, instead of testing strategies and methodology. I definitely believe testing and methodology is important, and potentially interesting, but I got into this line of business because I like building stuff and writing code. I didn’t get into it because I like to write tests and put Post It notes on a board. I would much rather design and build my dream house, than test the loadbearing capacity of the concrete being used, which is obviously important, but also very boring…trust me…I have studied that exact stuff…

Ok, let’s get back on track… What are those arguments that I disagree with? And what are the arguments I agree with? Well, here they are…

The number one crap argument for unit testing I get is “it produces code with less bugs”. Ehh… The code will not come out with less code, unless you write really good tests. And if you have the knowledge and ability to write those perfect tests, you would most likely have the knowledge and ability to figure out how to write the code without the test.

The second argument I don’t quite agree with is that it produces less code. Practicing TDD might cause you to write less code as you are not trying to write things that aren’t in the specs, but on the other hand, you write the tests, which are also built from code. And since ALL code needs to be maintained, it means that from a maintenance point of view, you need to combine the amount of code in the application with the code in the tests, which results in more code overall…unless you normally create a LOT of functionality that you shouldn’t.

I also argue that unit testing takes time, and I often get the answer that it doesn’t, which I strongly disagree with. Yes, if you do TDD, then you might write less code, which would be faster (see my comments regarding this in previous paragraph). And speaking to some more pragmatic and reasonable developers with TDD experience, it seems like most of them agree that you do end up with 10-20% more time spent writing the solution. They do often argue that this will be returned in the future when code changes are implemented and so on.

So the previous 3 arguments are the ones that make me throw a tantrum and argue that TDD and unit testing is useless and time consuming. Something I might not really agree with to be honest, but I just get so tired when people try to convert me and make me “see the light” using false arguments.

However, having talked automated testing with more people, I have finally got some really good, pragmatic arguments for at least unit testing and maybe even for TDD.

The first good argument is that well written unit tests work as documentation. A couple of well written and well named tests will clearly document the intention of a piece of code, so that a developer picking up the code can quickly get up to speed. And as the code changes, the tests change with it, so it always stays up to date. This kind of documentation is a lot better than a bunch of documents that no one reads, and rarely gets updated. It does however require you to name your tests appropriately and write them in a fashion that works for this purpose.

At some point, I think it was a session yesterday, someone showed a good example on how to do this for a method testing leap year logic. His example was a lot better than what I am about to show, but I suffer from crappy memory, so you will just have to make do with my abbreviated version… Imagine a method called IsLeapYear that takes a date and returns a bool. There are several ways to write tests for that method.

The worst version would be something like

public void IsLeapYearTest1()
public void IsLeapYearTest2()

Where IsLeapYearTest1 throws a bunch of leap year dates at the code and IsLeapYearTest2 throws a bunch of non leap years at the code. Not only are these named in a way that gives you no information at all, they are also not showing the intention of the code that is being tested.

Renaming them would at least solve one of those issues

public void IsLeapYearReturnsTrueForLeapYears() { … }
public void IsLeapYearReturnsFalseForLeapNonYears() { … }

Ok, so no they are named correctly and will test the implementation, so from a testing perspective, they are fine. But, from a documentation perspective, they suck big time. They don’t explain the requirements of the code at all.

Breaking them down to a couple of smaller tests, and naming them after the requirements is much better. And changing the naming from Pascal-casing to using underscores for spacing makes it easier to read

public void IsLeapYear_returns_true_for_years_divisible_by_4_but_not_100() { … }
public void IsLeapYear_returns_false_for_years_not_divisible_by_4() { … }
public void IsLeapYear_returns_false_for_years_divisible_by_100() { … }

As you can see, the above tests will test the code just as well as the previous example, but the naming makes it very easy to understand what the requirements are as well… Small difference, but big impact.

DISCLAIMER: The example I was shown used even better naming, but the important thing is not the precise naming, but that the naming is consistent and works as documentation as well.

Another interesting comment was that you shouldn’t use “should” in your naming. “Should” indicates that it SHOULD do something, but it might not. It doesn’t mean the same thing as “must” or “will” or something similar. So it literally means “it SHOULD do this, but in some cases it might not, which is also fine”.

The second good argument is about confidence. Adding unit tests gives you confidence that the code you are writing works. This doesn’t mean that you need 100% code coverage, but adding tests to areas where you are less confident will likely make you confident about the code and that it works as expected. This argument also does not argue TDD, it just says that adding tests will make you more confident when it comes to particularly complicated parts of you solution. It leaves the door wide open to do post-implementation tests…

And the final argument I really like, is that you can step in and test a bit of code without having to spin up the app and click through it to end up at the right point in the code to be able to test it. Being able to run a test and go straight into the code you want to validate is extremely helpful and speeds up development radically.

Then there are obvious one that everyone talks about. “It guides you to a better architecture of your application”. A unit testable solution is generally nicely decoupled and well architected. I agree with this, however I am not sure how I feel about this argument at this point in time. There are two things in particular that makes me ambivalent regarding this argument. First of all, can it be that people doing testing well are also more experienced developers, and thus bringing in that experience into the architecture? And secondly, I personally believe that most of my VMs for example, are completely testable and built in the way they should, without me using unit tests, which I guess brings me back to the previous point about experience…

That is pretty much the end of my rant, except for one last thing. I want to highlight the difference between unit testing and TDD. I know that everyone who knows anything about testing knows that they are 2 very different things. Unfortunately, I still run into people that have a hard time separating them. You can, and probably should, write unit tests for pretty much all code that you write even if you aren’t doing TDD. TDD is just a way of developing code using tests that has become popular. It relies on writing unit tests for everything you code, even writing the test before writing any code. But it is not the only way to write code that you test. Adding tests after having written the code works fine in my mind. It still works as documentation, and it still adds to you confidence, which means that it gives me both of the things I want to get out of unit testing.

Being a bit pragmatic and not a TDD developer, at least the moment, I believe that adding tests post development is a great solution. Especially since you can skip testing things you feel confident about, and add a lot of tests to complicated parts that you feel less confident about. This approach could probably save time and money in a lot of cases (if you believe writing unit tests takes time, which I think it does).

That was it for this time. Thanks for reading! Cheers!

Comments (2) -

The example you mention was from Kevlin Henneys keynote. And although I certainly believe in TDD as a general principle, I've seen way too many TestIsLeapYear1 not to realize that just because it's TDD doesn't necessarily make it good Wink

Thanks for the info! I thought so, but couldn't remember...

Add comment