.NN #8: Embedding Test Documents in Your Assemblies

On the current project I’m wrapping up we used the Visual Studio Team Test (VSTS) framework and Rhino Mocks to do our unit testing.  Mike Levy brought the idea of Rhino Mocks on board the project with him and it was the first I had been exposed to it.  I’ve been pretty impressed with it and I know we’ve just scratched the surface of what it can be used for.  We are moving our project to test now and it does so with over 1,000 unit tests from the UI (testing presenters since we used the MVP pattern with CAB), mid-tier and even database (we used Visual Studio Team Suite for Database Professionals which included unit tests for databases).  I have a firm belief that the unit tests have saved us time and energy since there were several times where we had to change direction or modify code in drastic ways as requirements changed.

One specific piece of our application consumed XML as a description of a set of tasks to perform.  Somewhat like a scaled down workflow system, but think of it more as a dynamic task executor.  In order to test the various scenarios and tasks this engine would perform we needed a way to feed it varying XML in our unit tests.  In production the XML would come out of the database.  I first started out just having XML files that were read in during the tests, but as Jim Holmes pointed out there can be some hoops to jump through to ensure that all of the files make it to where the tests are going to be run.  So instead of relying on external files that needed to be configured to be a deployment item, or ensure they are included in the Deployment options of VSTS we opted to embed the documents into the test assembly and create a helper to pull them out when we needed them.

Our particular task engine needed an System.Xml.XPath.XPathNavigator as a parameter which included the tasks to perform.  To test a particular task or to test the task execution we did the following:

  1. Added an XML document to the project using the normal “Add New Item” and selected XML file from the dialog.  We named the file appropriately (i.e., DataMoverTaskConfig.xml or ValidTaskExecutionConfig.xml). 
  2. We then made sure the XML file was marked to be an embedded resource by setting that on the file property.  Select the file in Solution Explorer and then bring up the properties window.  Set the build action to “Embedded Resource”.
  3. We then populated the XML document however we needed to for the test we wanted to run.
  4. Next we used a helper function to pull out the test document when we needed to in our unit tests.  Here is the code snippet:

public static XPathNavigator GetTestXmlNavigatorConfig(string testXmlFile)
{   
     XmlDocument doc = new XmlDocument();   
     doc.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream(string.Concat(“TestAsseblyNamespaceHere.”, mockXmlFile)));   
     return doc.CreateNavigator();
}

To use a given configuration our unit test would create an XPathNavigator by calling the helper function passing in the full XML file name to retrieve it:

XPathNavigator testConfig = GetTestXmlNavigatorConfig(“DataMoverTaskConfig.xml”);

The config could then be fed to the task or the task engine, depending on what you were testing.  Note that the helper function adds the namespace in front of the file name to be able to pull it out.

A couple of things here:

  • This method allowed us to test our configurations easily by allowing us to come up with whatever XML we wanted.
  • We didn’t have to mess with external files while testing.
  • We could have stored any type of file as an embedded file, but would need to create different helpers or make a more generic helper to deal with them.
  • We could edit our XML test documents as separate files easily enough.
  • Note that if you use this method and get a Null being returned or an exception when attempting to pull the resource the most likely cause is that you forgot to mark the file as an embedded resource :)  That hit me more than once.
  • I’ve thought of using this method to do things like embed javascript files in assemblies that generate javascript for user/web controls.  No more having to embed all that code directly into a string builder and deal with maintaining that mess.

Let me know what you think.