Data Files for Unit Tests

Recently I had an opportunity to try to explore a couple of different ways of putting data files into testing projects. Often you may have code that reads a config file, gets data out of a CSV, or modifies or reads some other file on disk. I have found that when considering the problem of getting the tests to run in a CI environment (TeamCity), using coding utilities that make testing a bit easier to run (ReSharper) and needing the test to work on other developers computers, that putting your data files as embedded resources is the way to go.

The alternative when using MsTest is something called the TestItemAttribute.  I first found this out on StackOverflow. Trying to use this methodology, I ran up against confusing and conflicting documentation and blogs regarding .testsettings that came in 2010 and still are somewhat compatible in 2012.  The replacement in 2012 is .runsettings which I also found had confusing documentation.  Many of the questions on these topics in StackOverflow are old enough to get people using them in 2010, with some trying to use the compatibilty in 2012.  After all this, the current version of Resharper (7.1.3) doesn't seem to be able to use the .runsettings so you end up with a test that works in the Visual Studio test runner but not Resharper's.   I didn't even get to the point of trying to get it to run in TeamCity.

properties.png

So back to embedded resources as a way of giving your unit tests something to work with.  The first step to this (after you have your testing project is to add the file(s) to that project.  Once you have added them, you need to change the Build Action property to Embedded Resource.  This basically copies the file into the DLL assembly that your project produces.

Now you need to get that resource available so your test can use it.  Generally you might be testing some other class that deals with files and you want to put a file into a temp location for the class under test to use it.  

class TestUtility
    {
        public static string WriteResourceToTempLocation(string resourceLocation,string directory)
        {
            System.Reflection.Assembly currAssembly = System.Reflection.Assembly.GetExecutingAssembly();

            Stream stream = currAssembly.GetManifestResourceStream(resourceLocation);

            var directoryPath = Path.Combine(Path.GetTempPath(), directory);

            Directory.CreateDirectory(directoryPath);
            var filePath = Path.Combine(directoryPath, resourceLocation);
            using (var fs = File.Create(filePath))
            {
                byte[] bytesInStream = new byte[stream.Length];
                stream.Read(bytesInStream, 0, bytesInStream.Length);
                fs.Write(bytesInStream, 0, bytesInStream.Length);
                fs.Flush();
            }
            GC.Collect();
            return filePath;
        }

        public static void CleanupTempLocation(string directory)
        {
            var directoryPath = Path.Combine(Path.GetTempPath(), directory);
            Directory.Delete(directoryPath, true);
        }
}

This class provides some helper methods that can be easily used in the [TestInitialize] and [TestCleanup] methods of your test class.

The last key to using this is being able to locate the resource in your dll.  The resource name and location will be appended to whatever the default namespace is as set in your project settings. This is generally what you named the project.

So if your project default namespace is "Foo.Bar.FileProcessing.Tests", and your testfile (testfile.txt) is located in a folder in your project named TestFiles.  then the resource location used in the above code would be "Foo.Bar.FileProcessing.Tests.TestFiles.testfile.txt"