Get Started Writing Testable Code
Testing is hard. Writing unit tests, regardless of if you write them first or last, is hard. This is because creating a design that allows you to write testable code is difficult at first – you have to break some old habits that you’ve relied on for a long time, and learn some new habits.
Many of the technologies we work with don’t easily lend themselves to unit testing: web pages, stored procedures, 3rd party controls. You’ll probably have to learn some new technologies that make testing practical, which also takes time and energy. You might have to write test barriers between the "untestable" part, and the logic; moving as much logic out of the hard to reach area and into easily tested code as possible. Which at a minimum represents more classes, more complexity, more typing…
Writing testable code is hard, and requires a shift in how you write code. This shift won’t come overnight. At first you will write "sort of" testable code, hopefully following some ideas laid out by other codebases (Various parts of Castle, ASP.NET MVC, and Rhino-Commons and other examples by Oren Eini are some good places to start). You will end up making mistakes, and a few months later you’ll look back at this code and wish you had done things differently. If you’re not having that experience with your code, then how have you improved as a developer? If you look at last year’s code and think that you’d change absolutely nothing, doesn’t that at least imply you haven’t improved your skills?
"But, we don’t have time to write unit tests." Does that mean that you have time to debug your application by stepping through hundreds of lines of code? Do you have time to litter your code with printf()s hoping to track down where a variable gets set to null? Maybe we’re just too used to the Edit-Build-Debug Cycle taking minutes for each iteration, waiting for ASP.NET to finish compiling its pages so we can navigate to where the problem is, enter what we think the bad data is, click a button, and then start hitting breakpoints… lather, rinse, repeat, for hours on end.
So when the project gets to the end of the timeline and budget, and the result is buggy, and we’re breaking more things with each deployment because of regression issues, and the client starts saying things like "this project is a total failure," and the project manager is demanding that everyone start putting in all-nighters to deal with the bug list … is that better than an alternative of spending a little bit more time up front on writing easily-tested code?
So, how about we strike a pragmatic agreement – identify a difficult part of an application you’re working on, and figure out how to write unit tests for it. I’m not even talking about "test first" or "100% test coverage," just write something to make it easier to confirm that the behavior of this code is correct. At least identify the parts of your code that stand in the way of tests – its almost always database or web-dependencies, and is an indicator of further problems, but we’re taking baby steps here. See how that goes, and see what you can learn from it.
I await your flames in the comments…
Uncategorized






No flames; you hit the nail right on the head! It’s much more efficient and sane in the long run to invest in unit testing, but many customers, project managers, and even some developers just see it as lost time not being used for deliverables.
To that end, non-technical stakeholders may not need to know about unit testing any more than they would know the details of design patterns. If you can fold unit testing into your usual way (and cost) of doing business, then you just have to get all the developers on board. Hopefully they quickly see the benefit after years of doing it “the hard way”
Great post Tony! No, flames from me, but then again, I’m a test guy.
I’ve been puzzled by why this is so challenging for some developers. Is the problem that they run out of time in the schedule? They don’t estimate for it prior to starting? They don’t know how to do it well? They don’t like to do it because it is not as enjoying as coding new functionality? They don’t understand the benefits? I suspect it’s all of the above at some level depending on the individual.
Then the question becomes, “How to we instill these best practices in more programmers?”
Great job breaking it down so it doesn’t have to feel so overwhelming. I’d be curious to hear more about what you’re using now. If I’m a .NET developer, should I look at ASP.NET MVC or is it not yet ready for prime time? And what are the 1st testing tools I should look at for ASP.NET?
And I love this: “If you look at last year’s code and think that you’d change absolutely nothing, doesn’t that at least imply you haven’t improved your skills?” If that’s not an argument for continuous learning, then I don’t know what is.
@abby: Regarding ASP.NET MVC, I think right now its very much “buyer beware” – I’ve used it on a couple of internal and test projects, but I’d hesitate to recommend it for a client’s project quite yet – once you start playing around with it you quickly realize that it’s not 100% “done” yet. There’s lots of good stuff in the MVCcontrib package, but there are also some things that should really be in the core and the team hasn’t gotten to it yet.
All that, and I’ve still enjoyed working with MVC more than Monorail
so who knows, right? Monorail is certainly a more complete and mature framework at this point.
If I was just starting out, using ASP.NET webforms, I’d take all of that code that ends up in your code-behind files and figure out how to move it into other more “service oriented” classes. The less code in the codebehind, the better. And now that I have it out of the webform, I’d start figuring out how to make it testable – decoupling from the data access for example, and using more descriptive objects instead of working with WebForm controls (don’t pass a TextBox reference around, do something meaningful with the Text instead…)
Tools that I like: TestDriven.NET to make running tests easier, either Spring.NET or Windsor for doing dependency injection, NUnit as a straightforward test framework, and RhinoMocks for when you get into the need for auto-mocking objects.