A friend from work started a conversation thread about whether or not there is value in unit testing and I wrote a pretty long response. And then I thought to myself… hmm, that could actually be a blogpost!! And so here we are, getting more value out of these precious keystrokes. Here it is the slightly rewritten version of why you should write unit tests…
I am definitely a pro unit testing kind of guy. I see it like this, in order to ensure that your code works as you expect you need to test it. In the olden days you may have done this manually, you may still test some parts of your application manually like the UI (parts where automated testing can be/feel too expensive), but the most cost-effective way to test is by writing automated tests, and particularly unit tests (test a unit of work/code in isolation). For me it’s usually about the short feedback loop, the instant and unequivocal knowledge that the code that I have just written works.
There is quite a score of benefits you can get from writing unit tests. Unit tests:
- ensure the code behaves as you want it to behave
- are fast to write, fast to run and provide a very short feedback loop to let you know that your code works
- help you find bugs with super-high granularity (if a unit test shows a bug, you pretty much know which class it is in, you don’t need to go throw all classes nor debug it, that’s priceless)
- force/encourage you to write very simple classes and increase modularity and separation of concerns (because writing unit tests for fat, complex classes with tons of collaborators is horrible and we have a natural tendency to avoid pain)
- increase developer confidence to refactor
- act as documentation for my future self and other developers
- ensure that a bug/defect never happens again (regression test)
- ensure that you don’t break something that was previously working
They are also a cornerstone for Agile development practices, without unit tests (and of course other types of automated tests) it becomes very hard to succeed with continuous integration and continuous delivery. How can you have confidence in that your application works as you expect if you have nothing to prove it? This becomes even more apparent when you have bigger application and the number of developers collaborating increases. (Although there’s people that thinks that the solution to manage complexity is to not write big applications in the first place xD). Anyhow, unit tests, and other types of automated tests, are one of the most basic fundamental elements that allows facebook or github developers to deploy to production several times a day.
At the same time, writing unit tests does not imply that you are going to catch all bugs in the universe with them or that you shouldn’t write other types of automated tests: acceptance tests, integration tests, end-to-end tests, UI tests. Different types of test have different costs/benefits and different “bug profiles” associated to them. Generally, high-level tests will provide a truer representation of the current state of your application which means high value to me, but they will typically have a higher cost to write and they will take a longer time to run (more setup, more cleanup, can be more brittle as well). You are also going to need to be ready to put some time to write unit tests and expect that you are going to write at least as much test code as production code, and see it as an investment that you can start taking advantage of instantaneously and into the future for ever and ever XDDD. Fortunately there are many tools that make writing unit tests easier and less of a hassle: testing frameworks (NUnit, MSTest, mbUnit, SpecFlow, NSpec, etc) , mocking libraries (RhinoMocks, Moq, FakeItEasy), testing utilities (AutoFixture, AutoMoq, etc), continuous test runners (NCrunch, MightMoose, etc), and so on.
If you want to be serious about testing (and about writing clean code), it is very important to keep your eyes open and exercise your awareness when writing unit tests. When you write unit tests you are exercising your own code, and you get a very good chance and are in a very good position to feel the pain, to experience whether your design is easy to use or a nightmare. TDD it is particularly interesting in this regard, you get to be the first person to use your own code. Do you feel pain when using your code? Fix it, refactor it, redesign it till you get to something that is pleasing. Do you update some implementation and break a bunch of tests? Perhaps you tests are too specific, focus on testing behavior, the appreciable results of exercising a piece of code. Test, reflect, improve (your tests and your production code).
Some Awesome Resources to Learn About Unit Testing and TDD
- The Art Of Unit Testing 2nd Edition (Roy Osherove): Great introduction to unit testing
- Test Driven Development By Example (Kent Beck): Great step by step introduction to TDD as if it were a pair programming session
- Growing object oriented software guided by tests (Steven Freedman): More in depth and developed approach to organically grow an application through TDD
- Professional Test Driven Development in C# (James Bender)
Articles and blogs
- Mark Seemann’s blog has a ton of articles and some videos on TDD and unit testing
- Roy Osherove’s blog
- Advanced unit testing by Mark Seemann
- Outside-In Test-Driven Development Mark Seemann
- Roy Osherove’s TDD Masterclass
And those were some thoughts from the top of my head on why I think that you should write unit tests :).
Other interesting articles on the subject: * Test Behavior not Implementation * Barbaric Book Review: The Art of Unit Testing 2nd Edition * Write 30% Less Test Code With AutoFixture