Tuesday, January 09, 2007

NUnit: deployment items with ReSharper/TestDriven.NET

I like my solutions to be self-contained, that is - by just checking out all files from the solution root in the source control management (SCM) system, the solution should be able to compile and run. This includes storing the referenced assemblies and their dependencies in the SCM: "everything you need to do a build should in there including: test scripts, properties files, database schema, install scripts, and third party libraries" [Fowler: Continuous Integration].

And as the unit tests should be treated as first class citizens in a solution, the SCM best practices must apply to the test project also. Adding the referenced assemblies to a NUnit test project is straightforward, but how do you add the dependencies of the references (i.e. the assemblies referenced by the referenced assemblies, and so on) ? For those using a native NUnit project this can be configured using the "Assemblies" tab in the "Project Editor".

For those using ReSharper and/or TestDriven.NET, just adding both the referenced assemblies and all their dependencies to the Visual Studio class library "NUnit" project will work. The downside of this is that you will get more object models/namespaces to choose from when coding your tests, and even if ReSharper somewhat helps you pick and create "using" statements, it can be confusing and thus a source of errors.

VSTS has a nice solution to this "extra binaries" problem, the deployment item mechanism, which I have used in other projects. So this is how I implemented support for using a \DeploymentItems\ folder in my NUnit test project (note that my build output folder is just \bin\ without debug or release):

[TestFixtureSetUp]
public void TestFixtureSetup()
{
foreach (string file in Directory.GetFiles(@"..\DeploymentItems\", "*.dll"))
{
string newFile = Path.Combine(@"..\bin\", Path.GetFileName(file));
if (File.Exists(newFile)) File.Delete(newFile);
File.Copy(file, newFile);
File.SetAttributes(newFile, FileAttributes.Normal);
}
}

Note that the file attribute is set to normal after copying it, this is to remove any read-only attribute on the assembly to ensure that it can be overwritten the next time the tests are run. Afterall, the deployment items must also be source controlled and will thus be read-only when you do a "get latest version" from your SCM.

The DeploymentItems folder is not limited to assemblies, just change the GetFiles filter to copy other items to the test execution location as well.


The solution is inspired by Scott Hanselman's post about unit testing with Cassini.

No comments: