Tuesday, July 03, 2007 10:10 PM
Right now we're in the midst of that 'great search for sample data' that all new projects go through. As part of alleviating this search, we're in the process of building our own sample data generator to create our own reams of data. And since we're developing this using-you guessed it-test driven development we've been able to construct a full suite of unit tests around the utility.
In fact, one of the cool things that came out of this was getting to use a very cool testing pattern. You see this generator doesn't actually create the test data directly, it actually creates a series of SQL scripts that we use to populate the database for us.
One our business rules involves ensuring that our resulting SQL file is never appended to and instead that it's always completely overwritten. In essence, we're only testing the code below...
if (File.Exists(_testFile))
{
File.Delete(_testFile);
}
You see, the way that we were testing that the file was being successfully generated was by simply opening it, reading the file in it's entirety, and then verifying that the file contained the expected number of lines. If the previously existing file was not deleted at the beginning then the the resulting number of lines would be greater than expected...at least twice that, to be exact. Since this was also the same logic that we were using the test the file generation elsewhere this brought up the issue of how to reuse the code. Granted we could factor this code into a common utility, but I like to keep my unit testing code as straight forward as possible since we later use the code for developer documentation. So, using some of the extended attributes of
MbUnit, we came up with a bit more elegant solution.
This is the original test to ensure that the file is generated correctly. Note that in this test, we're randomly generating an output filename at the beginning of the test and then cleaning it up at the end. This ensures that the file will never be previously existing on disk since the point of this test is to ensure the file is written correctly, not that it is
overwritten correctly.
[Test]
public void SqlScriptCanBeWrittenSuccessfully()
{
string targetFileName = Path.GetTempFileName();
int expectedNumberOfLines = 1255;
GenerateSqlFile(targetFileName);
Assert.AreEqual(expectedNumberOfLines, File.ReadAllLines(targetFileName).Length);
File.Delete(targetFileName);
}
However this randomly generated file name makes it impossible to test if the file can be successfully overwritten in later tests. Impossible, until you remember that MbUnit offers parameterized unit tests. By simply altering the signature of the test, we can (optionally) pass a filename into the test via MbUnit's
RowTest attribute. As you can see from the code sample below, if no file name is provided then we simply generate one using
Path.GetTempFileName().
[RowTest]
[Row(null, true)]
public void SqlScriptCanBeWrittenSuccessfully(string fileName, bool deleteOnCompletion)
{
string targetFileName = fileName ?? Path.GetTempFileName();
int expectedNumberOfLines = 1255;
GenerateSqlFile(targetFileName);
Assert.AreEqual(expectedNumberOfLines, File.ReadAllLines(targetFileName).Length);
if (deleteOnCompletion)
File.Delete(targetFileName);
}
Now, to ensure that our file can be successfully overwritten all we have to do is pass a static file name into this very method from another test. When coupled with MbUnit's
RepeatTest attribute we can run the test multiple times against the same file to prove that it is in fact completely deleted prior to continuous runs.
[Test]
[RepeatTest(2)]
public void SqlScriptWillBeOverwrittenIfPreviouslyExisting()
{
string targetFile = @"C:\Temp\output.sql";
SqlScriptCanBeWrittenSuccessfully(targetFile, false);
}
This is functionality that comes straight out of the box with MbUnit that would otherwise be difficult if not impossible to do with other testing frameworks. Nice job guys.