Archives for the month of: September, 2012

Why I don’t write unit tests

Unit tests have saved me countless number of times. I know the benefits of TDD. Yet there are times when I do not write unit tests because writing unit tests takes time. And my good senses have a tendency to desert me whenever I am racing against a deadline. So I cut corners, leave out unit tests, and of course, pay for it later in production. This got me thinking – which parts of authoring UTs are the most time consuming? And can I do something about it? Turns out writing mock objects is one of the most time consuming parts of authoring UTs. And yes, there is something I (and you) can do about it.

Common unit test pattern

Lets say you have a simple 3-tier architecture that is most common for web applications.

Now, I want to write a unit test for the SnackController. I would end up writing 2 pieces of code – the actual unit tests for the SnackController and a mock CookieService.

Why mocks?

  1. I want to test how SnackController behaves when CookieService throws unexpected errors and faults. In other words, I want to inject faults in CookieService.
  2. If CookieService is a remote process, usually running on a different server, I don’t want my SnackController unit tests to take a dependency on network I/O or RPC. I want my unit tests to be “self contained” and fast.
  3. I don’t want bugs in CookieService to affect the testing of SnackController.

Reason #1 is the strongest reason why I write mock objects. For reason #2 – I am ok with making calls over the wire in my unit tests. Sure, they will fail occasionally due to network flakiness or other reasons outside the control of my test environment. But if it can happen in a test environment, it will surely happen in production! So I better have the right recovery actions in place in SnackController. And as long as I run CookieService unit tests before running SnackController unit tests, I am reasonably assured that reason #3 will not be an issue.

Proxy instead of mock

Now, instead of writing a mock service for the purpose of injecting faults, wouldn’t it be cool if I were to write some sort of a proxy object with delegates (function pointers for the old schoolers amongst us :-)) that are wired up to methods in the real CookieService? That way I could re-wire any delegate in the proxy to a faulty method at runtime and inject fault in the SnackController calls.

Auto-generated proxy

Sounds cool. Now I can switch between faulty and actual behavior at runtime. But it does not solve my original problem of saving time. I ended up writing a proxy service instead of a mock service – spending just as much time as before. What I really need is a proxy factory that can create – at runtime – the above proxy object, with the delegates and all. Turns out with a bit of IL magic, I can do just that. And I did 🙂 With two lines of code, I can create a proxy object for any given object, implementing any given interface.

ProxyFactory pf = new ProxyFactory(false);
ICookieService proxy = pf.Create(realCookieSvc);

By default, the proxy object implements ICookieService and all its methods are wired up to the corresponding methods in the realCookieSvc. ProxyFactory is the class that I wrote that has all the IL magic in it.

ProxyFactory has a ChangeBehavior method that can rewire any method in the proxy to a user supplied method. So in this case, I would implement a faulty method ask ProxyFactory to rewire one of the good methods in the proxy to the new faulty method. All of this happens at runtime.

You can find the code and this example (in much more detail) implemented in the dynamic_proxy project on github. This is under the creative commons license, so do as you please with it :-).

But that is not good design..” is a refrain I have heard in countless software design meetings arguments. And usually these arguments devolve pretty quickly into philosophic disagreements based on opinions rather than facts. This obviously begs the question – so what is good software design? I’ll add my own opinion to this ongoing discourse with 7 design principles of good software design.

1. Functional
A few months ago I was at a Lean Startup Meetup where one of the panelists was the VP of technology of a well known company that had just acquired a smaller company. Software architects working for her were commenting that the design of the acquired software was terrible, when one of them had the sudden “insight” – “but we just paid millions of dollars for this, because the shi*t works!” And that to me is the first and foremost principles of good design. Any piece of software must be able to execute its core functionality well. Otherwise it is all for naught. Good software is functional.

2. Robust
I was showing off my mad coding skillz in asp.net/mvc to a friend of my mine who happens to be a software architect. He gave me a kind fatherly look and said “this is all fun and good, but can you write code that will live on for years and years? This phone that you are using has a piece of code that I wrote 5 years ago”. Which brought me to my second epiphany about good software design. Its got to be robust. But what does robust really mean? To me it means three things – software is resistant to failures, it is able to recognize when failures occur, and it is able to recover from failures. Good software is robust.

3. Measurable
My third design principle was not a sudden a-ha moment for me. But rather it grew on me gradually, as a result of working in environments that were extremely data driven, sometimes ruthlessly so. It should be possible to measure how the software is doing out in the wild, outside of the confines of my test fixtures. Of course the actual measurement metrics would differ based on the business and nature of the software. An oft used metric for web applications is the number of HTTP 500 ISEs that are returned. For UI based applications, it is usually the number of seconds for the UI to respond. But if you find yourself writing software without putting any thought as to how to measure it (whatever measure means for you), know that you are violating a good design principle. Good software is measurable.

4. Debuggable
I remember the days working as a developer in an e-commerce company (yes, that one), where I would get paged because the ordering system was misbehaving. I would be the only developer debugging the live system, all the time surrounded by a bunch of manager-types who would demand “status” every second minute. Walking through the code in that situation was – to put it mildly – not fun. And it brought home to me the realization that any software I write should have logging that is not just there for the heck of it, but that would really help me debug when the need arose. Since then I have put in debug APIs on my web services, put in ability to dump debug state upon demand, etc. Good software is debuggable.

5. Maintainable
One of the ways in which recruiters try to attract software developers is by telling them that in the new company, they will be writing brand new software from scratch. Which highlights the fact that a lot of work that software developers do is maintaining software that somebody else wrote a while back. Software can be easy to maintain if has consistent styling, good comments, is modular, etc. In fact, there is a lot of literature on good software design that just focuses on design principles that make it easy to make changes to parts of the software without breaking its functionality. Good software is maintainable.

6. Reusable
Software developers worry about writing reusable software incessantly. A lot of software developer interviews that I have sat in (as an observer, interviewer, interviewee) invariably pose an algorithm question – “how can you find the least common ansector in a tree”, then the follow up is – “how can you make it more general”. But note, that this is #6 on my list. That is because generilizing a specific solution is hard and time consuming. And a lot of times nobody ends up reusing that piece of code anyway (of course you can argue that it is because the code was not reusable enough to begin with…). So unless you are absolutely sure that you are going to reuse this piece of functionality elsewhere, and you can time bound the effort of making it reusable, do not try this at home. Nevertheless, good software is reusable.

7. Extensible
Surprisingly, the topic of maximum number of design arguments I have sat through makes it last in my list. Usually, this is brought up when you are building infrastructure software. And it usually starts with – “but suppose that tomorrow somebody wants to add X here…”. Be wary of this design principle. Be very wary. Unless “tomorrow” is a real date in the future, and “somebody” is a real person, this design principle can lead you down a deep rabbit hole with nothing but dirt at the bottom. But I have to say this – really good software is extensible.