Before I wrote a line of code for cmeScribble I decided that I'd code
it the right way. Retrofitting "right-way-ness" onto an existing
project is almost impossible, so I was determined not to miss the
chance to build it in from day one.
The particular "right way" I had in mind was using Test-Driven
Development, or TDD. For anyone unfamiliar with the TDD methodology,
the idea is that before you write any code or fix any bug you first
write a test or two to make sure the code works or the bug is fixed.
The test should fail when you first run it, because the code doesn't
work and the bug hasn't been fixed. Once you've written the test and
verified that it fails, then you write the code to make it pass.
The advantages of TDD are well-documented elsewhere, but in a nutshell:
writing the test forces you to know in advance exactly what you want
the code to do, and also as you develop more and more code you have a
library of tests to make sure you don't break anything.
I could easily see how to write tests for self-contained functions, but
I couldn't figure out how to write tests for user interfaces or
database-backed logic. Since cmeScribble is all about building user
interfaces from database data, this posed something of a challenge.
Maybe I can get away without testing the UI layer, but I can count
features that won't need database data in some way on the fingers of
Captain Hook's right hand. Okay, I thought, Google to the rescue... but
searching for databases and TDD provided lots of people with similar
issues and no straightforward answers.
A common suggestion was to use "mock objects". The idea is that you
create a "fake" version of your database layer which returns plausible
values without actually needing a database. This solves the problem but
the downside is that you have to actually write the mock version of
each database table, and since cmScribe has over a hundred tables at
last count[1] I didn't relish that thought much either.
Facing a potential deal-breaker for something you don't want to
compromise on tends to focus the mind, and eventually I hit on a
solution. A full SQL database is hideously complex, but cmeScribble
will be doing it's database access through nrdo (I'm biased, but I
honestly don't see how people can work any other way...) and for 99% of
uses, nrdo provides a fairly simple object model over the top of the
database. Simple enough that if you don't care about persistence (which
in a test situation is actually a bad thing) or performance, it's
possible to implement the vast majority of it very simply in memory, as
part of the nrdo runtime library and generated code, backed by simple
List<T>s. A couple of days of hacking later and that's exactly
what I've now checked into nrdo's CVS: If you use the C# 2.0 template
and set the configuration setting "NrdoMock" to "True", all your code
will run purely in memory, with a clean "database" for each run.
There are a few things missing - at a wild guess I'd say it covers 60% of
what you'd want to do right now, and I should be able to get that up
to about 90% without too much trouble by filling in things as I need
them (after writing tests!). Most of the rest can't be handled
automatically (because they'd require a full SQL engine in the mocking
layer) but can be done with a little manual intervention (by the
programmer providing a C# equivalent of the SQL clause in question; the
SQL clause itself doesn't get tested but all the logic around it does).
The most satisfying part of the whole thing was watching it pass the tests I'd prepared in advance :)
[1] cmeScribble will have a lot less because of the "enterprise"
features that aren't being included and also hopefully by unifying lots
of things that were separate tables in cmScribe, but still.