Deploying test data for MSTest
MSTest, the unit testing framework from Microsoft that ships out of the box with the last few versions of Visual Studio, has a great feature whereby tests run through Visual Studio are not run in the output path of the project (like other common testing frameworks in the .NET space) but are rather copied after build to a TestResults folder with a unique name and run there.
This feature allows you to execute multiple test runs over time while retaining the binaries and results of previous runs for historical comparison.
Unfortunately, project content (i.e. external test data such as databases, xml files, spreadsheets, etc) is not copied to these test run folders even when the Copy to Output Directory property is set to Copy Always and the content appears in the project’s output path.
A quick poke around the net reveals that this little feature has tripped up many a developer and I could not find a well-known, reliable workaround.
The two approaches that seem most commonly represented each have their problems:
- I’ve tried specifying the content files in the Test Settingsdialog, but that doesn’t always work, and you have to specify individual file paths, not directories. When you have the odd content file required by your tests that may not be a big deal, but as the number of content files scales this method does not.
- I’ve also tried using the [DeploymentItem] attribute but this is a method-level attribute which means that you have to specify it on each method that uses the deployed item. Again, this is an approach that works passably well with a few files but becomes quickly unwieldy when you have more files to manage.
The approach I’ve settled on lately is to embed the content files as resources within the DLLs that need them, then extract them at run-time so that I know exactly where to find them.
Embed your content and extract on demand
Embedded resources are packaged up into the output binaries which are happily shipped over to the TestResults folder by MSTest.
Set the Build Action of your content files to Embedded Resource.
Then you can do something like the following in your test class:
[csharp] protected void InitializeDatabase() { ExtractManifestResourceToDisk("Data.Catalog.sdf", @".\Data\Catalog.sdf"); }[/csharp]
Voila! You are now in programmatic control of exactly where, when, and how often your test data gets deployed.
Here’s our implementation of that helper method:
private static void ExtractManifestResourceToDisk(string relativeManifestUri, string targetPath) { var assembly = Assembly.GetCallingAssembly(); if (File.Exists(targetPath)) return; var targetFolder = Path.GetDirectoryName(targetPath); Directory.CreateDirectory(targetFolder); var uri = String.Format("{0}.{1}", assembly.GetName().Name, relativeManifestUri); using (Stream input = assembly.GetManifestResourceStream(uri)) using (Stream output = File.Create(targetPath)) { input.CopyTo(output); } }
Hope you find this useful!
This post brought to you by MSTest, TDD, and a local blogging challenge whereby each of the talented technologists listed below join me in committing to posting a minimum of once every two weeks. Check them out!
- Tyler Doerkson: http://blog.tylerdoerksen.com
- Aaron Kowall: http://www.geekswithblogs.net/caffeinatedgeek
- Steve Rogalsky: http://winnipegagilist.blogspot.ca
- Dylan Smith http://geekswithblogs.net/Optikal/
- Dave White: http://www.agileramblings.com