An Obsession with Everything Else

http://www.derrickschneider.com/atom.xml

Saturday, September 27, 2008

Testing Testing

Since I claim to advocate Agile development practices, I recently decided to give unit testing, the writing of simple test suites that exercise an application's basic functionality, a solid try.

I've been in various organizations that have tried to do it, but the effort always founders and fails. Everyone knows unit tests are a good idea; no one ever gets any in place. When the subject comes up at job interviews, other companies seem to have had the same problem. New features always creep ahead in priority, and once the code becomes too big, writing unit tests becomes too daunting a task. Plus, the new feature is working, more or less, so the motivation decreases to write tests for it.

As I started my Mac/iPhone programming, however, I realized I had the perfect environment for unit testing: no deadline and no legacy code. So I wrote new code and the test cases to exercise it at the same time. (I didn't do test-driven development, where you write the tests first and then write the code to make them work.)

I haven't since become a unit testing zealot — at least I don't think so — but I have a renewed enthusiasm. In a normal development scenario, I would have written all my support classes and then a UI so that I could test them. If there was a bug, I'd have to step through all the layers I had written to find it. With unit testing, I found several bugs long before I had a UI. When I did attach a UI, the fact that it worked smoothly out of the gate was anticlimactic: I knew the underlying structure was solid, after all.

Recently, I thought of some code reorganization I could do on the project as well as some significant performance improvements. I spent an hour or so doing the work, shuffling this here and that there. I set up my unit tests to run, and they passed: I could assert that my changes hadn't broken anything major. That's a really good feeling; there was a bit of a rush when I saw "Build Succeeded" flicker by in the log.

Even with my short experiment, I realized the two major benefits of unit testing: catching bugs earlier ("The earlier you catch bugs, the cheaper they are to fix," runs one programmer mantra) and making sure that major code changes don't affect the core functionality.

But how to bring that to work? At Maxis, we have a lot of pressure to deliver features, an always-busy team, and a big ball of legacy code even for the website. Just like every other tech company. One of my co-workers and I have talked about it, and the only answer seems to be: just start writing them, and keep them running. The first few will be tough, because you need a support structure for them (especially in the form of mock objects, layers that conform to the contract of the real layer, but deliver static, simplistic data), but once they get going, hopefully it becomes easier to add new ones. I have a squirrely bit of logic in one piece of code: That's my first candidate.

The problem is selling the concept to the business side. I think in a lot of companies, the extra time spent writing unit tests is seen as time that could be spent writing a new feature. In a way that's true, but I now see that unit tests actually are a feature: stability. No one wants an unstable system, but the investment in stability doesn't obviously pay off for a long time. In fact, if you've done it right, the investment never seems to pay off because the system doesn't have as many problems. You can't measure how many problems there would have been if the investment hadn't been made. But you can measure the number of users using a new feature. So you get instant feedback that it was a good addition, and you provide a metric. How does unit testing compete against that?

Labels:

2 Comments:

At 10:11 AM, Anonymous Anonymous said...

One way to improve motivation is to run all of your reasonably quick unit tests during the nightly build (you have one, right?) and then measure the code coverage you're getting with them. You'll find vast swaths of the code that aren't even being exercised, and you'll be motivated to get those coverage numbers up.

This is a two-edged sword, though, since simple coverage just says that you exercised the code, not that anything was checking the results for correctness. This can mean that you end up with a suite of end-to-end tests, not unit tests, which is better than nothing but not as helpful in supporting the kinds of major refactorings you discussed above. Still, that's a much better problem to have than no automated coverage at all.

 
At 10:42 AM, Blogger Derrick said...

Actually, we have an hourly build. Now. We didn't have any when I started, though, honestly, we rarely break the compile-ability of the code.

I've seen some tools that do the type of reporting you mention. I worry that it would be demotivating at the beginning because it would seem like such a monumental task -- we have no unit tests to speak of at the moment. Have you used them against a code base with no tests? (This is all on the website; the game has a full-time automation engineer [who is, sadly, not a resource we can use immediately] but they're also a 30-40-person dev team, not a 4-person dev team.)

And yeah, I think we'll also need to review each other's tests to make sure we're hitting correctness first and foremost. No "assert true" at the end of a long block of code :)

 

Post a Comment

<< Home