Monday, April 13, 2009

Copying dependency files to the output directory when running unit tests with MSTest

A rather long title don't you think :) But have you ever wanted to write a unit test using Visual Studio Test Edition and MSTest to have a dependency on configuration files other than app.config to fulfill your test?

This post is mainly about test support for device testing but is the same for desktop testing too.

Now, most people use app.config as a configuration file in .NET for desktop applications. On devices, System.Configuration is not supported. So some device developers end up writing there own configuration reader by serializing the XML into an object. Alternatively they use the class from OpenNETCF SDF or they might parse the XML using LINQ to XML or plain old System.Xml.

Personally I tend to use the serialization option. My configuration files are not named app.config I tend to name them something more specific. You'll find Visual Studio or the test engine does not deploy any other file to the tests Output directory, even if you set properties Build Action to Content and Copy to Output Directory to Copy always. Running your test will fail if you have a dependency on the given configuration file as Visual Studio will not deploy the custom configuration file. It is highly likely most desktop developers have never seen this or unaware this limitation exists because most desktop devs use app.config. In most cases for desktop devs there is no problem.

So how do device guys get around this problem? You can use the same technique as the Mobile p&p team do with the DataAccess block which forms part of the recent Mobile Application Blocks drop: http://www.codeplex.com/Mobile. In one of there test projects they have a mock database dependency that is tested on the desktop but need a sample database to test against.
They set the Build Action to Embedded Resource then they use reflection to unpack it. You might think this is quite a bit of work, but they have a reusable class named TestResourceFile that belongs to the TestUtilities project. Using this class does all the unpacking for you.

The code to copy this dependency could look like the following (I've taken this from the p&p Mobile codebase):
private TestResourceFile CreateDbFile()
{
dbFile = new TestResourceFile(this, "MockDatastore.sdf");
connectionString = String.Format(connectionStringPattern, dbFile.Filename);
return dbFile;
}

[TestMethod]
public void ThrowsIfNullParameterNameIsPassed()
{
using (TestResourceFile file = CreateDbFile())
{
using (Database database = new SqlDatabase(connectionString))
{
Database service = new SqlDatabase(connectionString);
ExtendedAssert.Throws(
delegate { DbParameter param = service.CreateParameter(null, "Maria Anders"); });
}
}
}
But even still there is a better way to solve this problem and that is to use the DeploymentItemAttribute class. It's use is simple. The following code illustrates it use:
[DeploymentItem("Mobile.DataMapper.config")]
[TestClass]
public class DataContextFactoryTests
{
}
The above code will copy the file "Mobile.DataMapper.config" to the test output directory. You need to ensure to set the file properties Build Action to Content and Copy to Output Directory to Copy always.

I've got to say thanks to Chris Tacke for telling me about this attribute class.

5 comments:

Daniel Marbach said...

Hello Simon,
Thanks for this article. I personally use NUnit with Resharper. Resharper's TestRunner had the same problem. The problem was that by default resharper does shadow copying of the test assemblies but without the dependencies such as configuration files eventhough set copy always and build action to content. With Resharper Configuration it is possible to disable the shadow copying of the test assemblies and then this approach works without the using your described tricks...

Daniel

Simon Hart said...

Hi Daniel,

I used to use NUnit (great tool) and great integration with ReSharper. Except one thing, the reason we use MSTest is for the integration support in TFS. NUnit doesn't have TFS integration support for continous integration.

I admit I don't like the integration of MSTest in Visual Studio it's not very user friendly. Although I noticed ReShaper 4.5 has built in support for MSTest in VS so will have to wait and see what its like as currently it is in beta.

Simon.

Daniel Marbach said...

Hello Simon,
Sorry I'm becoming a spammer of your posting section ;) Resharper 4.5 is final :D Check out the website

Daniel

Simon Hart said...

Hi Daniel,

So it is. Thanks. Looks really cool too. I like the MSTest integration. In terms of performance, I'll need to check that.

Regards
Simon.

Anonymous said...

That was very useful, thank you Simon