Sunday, 7 April 2013

Testing Web.Config Transforms with ApprovalTests

In this post I will show how you can write a test with your favourite unit test framework that will apply your web.config transformation and compare the result to a master. ApprovalTests will make it easy to update the master when you make deliberate changes and will alert you when you make inadvertent changes.

Why would you want to do this?

Normally when you use config transformations you have to use something like SlowCheetah to check the output and it relies on you actually doing this. With this test you have an explicit test that will force you to review the impact of your changes. In my personal scenario, I have a rather complex build script that applies multiple levels of transforms to several config files, including Azure service configs, web.config and app.config files so it becomes quite important to be able to keep track of the impact of any change.

How do we do it?

There are three things we have to do;
  1. Get the web.config file into your Unit Test project in the first place
  2. Apply the Transform file
  3. Compare the output to a master and update the master as required
Note that I am using MS Test in this example – you can use NUnit. XUnit and others depending on your preferences.

Get web.config into your unit test project

Your unit tests may run in a predictable directory when you run them through the test runner in Visual Studio. But when you run them through things like NCrunch, the tests will actually run in a completely different place. In order to handle this, we will include a link to the web.config files in our unit test project and specify that the file should be copied to the output directory when building.
  1. Create a folder for the test in your Unit Test project. In this example we will use “ConfigTest”
  2. Right-click on the folder and select “Add” –> “Existing Item”
  3. Browse to the Web.Config file in your web project and, on the Add button, select “Add As Link”.
  4. Right-click on the linked file in your unit test project and select Properties, then select to Copy to Output Directory.
  5. Repeat for the Web.Release.Config file (or whatever transform you are testing)

Apply the Transform

In order to apply the Transform, you have to first add the NuGet Package Microsoft.Web.Xdt package. It is pre-release at this time, so use this command:
Install-Package Microsoft.Web.Xdt –pre
This will allow the code-listing below to transform the file and write the output to a string variable.

Compare the result to a master

ApprovalTests is quite a different way of doing assertions and I do recommend you check it out. In short and for our purposes, it will show you the result of the transform and, when you are happy with it, will save it as a master. Whenever your result changes, the unit test will start to fail and if you run in interactive mode, you will get the chance to save the result as a new master. See xxx for more details.

All we have to do is add the Nuget package for ApprovalTest to our unit test project:
Install-Package ApprovalTests

Finally, the code

namespace Specs.ConfigTests
{
    using System.IO;
    using System.Reflection;
    using System.Xml;

    using ApprovalTests;
    using ApprovalTests.Reporters;

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Microsoft.Web.XmlTransform;

    [TestClass]
    [UseReporter(typeof(DiffReporter))]
    public class Apptest
    {
        [TestMethod]
        public void Test()
        {
            // Arrange
            var binPath = Assembly.GetExecutingAssembly().Location;
            var inputFilePath = Path.Combine(Path.GetDirectoryName(binPath), "ConfigTests\\Web.config");
            var transformFilePath = Path.Combine(Path.GetDirectoryName(binPath), "ConfigTests\\Web.Release.config");
            string result;

            // Act
            using (var input = new XmlTransformableDocument())
            using (var transformer = new XmlTransformation(transformFilePath))
            {
                input.Load(inputFilePath);
                transformer.Apply(input);

                using (var stringWriter = new StringWriter())
                using (var xmlWriter = XmlWriter.Create(stringWriter))
                {
                    input.WriteContentTo(xmlWriter);
                    xmlWriter.Flush();
                    result = stringWriter.ToString();
                }
            }

            // Assert
            Approvals.VerifyXml(result);
        }
    }
}

When you run this test interactively, ApprovalTests will fire up whatever diff tool you have installed and you can then save the current version as the new master by “merging” in the diff tool. The details will depend on your diff tool.