We have been busy with BizTalk 2013 recently and like all good programmers we like to test, test test!

BizTalk 2010 made this quite a bit easier as you can enable unit testing on your schemas and maps. However, we hit a problem with the supplied TestableMapBase class whilst testing maps with external function calls. I will run through the process from start to finish to demonstrate the issue and the fix.

We have a very simple schema and map setup:

bizTalkBlog1

For which we enable unit testing in the Deployment tab of the Project properties:

biztalkBlog2

Now we create a unit test properties and add a reference to our schema / map projects and the following BizTalk dlls (ignore the Fakes assemblies for the moment):

bizTalkBlog3

Once this is setup, we can test our map in code as follows:

        [TestMethod]
        [DeploymentItem("Input.xml")]
        public void NoLookup()
        {
            var target = new Test();
            var map = new NoLookup();
            var output = new Test();
            var source = "Input.xml";
            var outFile = "Output.xml";

            Assert.IsTrue(target.ValidateInstance(source, OutputInstanceType.XML));
            map.TestMap(source, InputInstanceType.Xml, outFile, OutputInstanceType.XML);
            Assert.IsTrue(output.ValidateInstance(outFile, OutputInstanceType.XML));
            TestContext.AddResultFile(outFile);
        }

Note we have to add the output file to the text context otherwise it will be deleted when the test ends. See http://msdn.microsoft.com/en-us/library/ms404699(v=vs.80).aspx

Ok, that is one map tested. Let us try a different map that uses an external function call, specifically the GetCommonValue functoid. This looks up BizTalk Xref data from the management database:

BizTalkblog4

If we now run the equivalent code for this map, we get a rather unhelpful exception:

Microsoft.BizTalk.TestTools.BizTalkTestAssertFailException: Transform Failure
Result StackTrace:
at Microsoft.BizTalk.TestTools.Mapper.TestableMapBase.PerformTransform(String inputXmlFile, String outputXmlFile)
at Microsoft.BizTalk.TestTools.Mapper.TestableMapBase.TestMap(String inputInstanceFilename, InputInstanceType inputType, String outputInstanceFilename, OutputInstanceType outputType)
at TrueNorth.BizTalk.Blog.Testing.MapTest.Lookup()

It doesn’t like our external lookup functoid. With some digging we uncovered the ‘real’ exception:

System.Xml.Xsl.XslTransformException: Cannot find a script or an extension object associated with namespace ‘http://schemas.microsoft.com/BizTalk/2003/ScriptNS0’.

The issue is with Microsoft.BizTalk.TestTools.Mapper.TestableMapBase class, specifically with the PerformTransform method:

    private void PerformTransform(string inputXmlFile, string outputXmlFile)
    {
      XmlReader input = (XmlReader) null;
      XmlWriter results = (XmlWriter) null;
      try
      {
        XslCompiledTransform compiledTransform = new XslCompiledTransform();
        XmlReader stylesheet = (XmlReader) new XmlTextReader((TextReader) new StringReader(this.XmlContent));
        XsltSettings settings = new XsltSettings(true, true);
        input = XmlReader.Create(inputXmlFile);
        int num = (int) input.MoveToContent();
        results = XmlWriter.Create(outputXmlFile, new XmlWriterSettings()
        {
          Indent = true,
          IndentChars = "\t"
        });
        compiledTransform.Load(stylesheet, settings, (XmlResolver) null);
        compiledTransform.Transform(input, results);
      }
	  /* snip */
    }

The instance object for each external function should be associated with the correct namespace in an XsltArgumentList object. This should then be passed to the Transform method on the last line:

compiledTransform.Transform(input, this.TransformArgs, output);

works correctly.

So now we know the problem, how do we fix this? Well, the easiest way is to ‘hack’ the XslCompiledTransform method call to do what we want for the purposes of the test. We can do this using Microsoft Fakes (http://msdn.microsoft.com/en-us/library/hh549175.aspx).

First add a reference to System.Xml. Then r-click the assembly and select ‘Add Fakes…’

bizTalkBlog5

We can now inject code into any of the method calls for the faked assembly. The magic code for us is:

            using (ShimsContext.Create())
            {
                var target = new Test();
                var map = new Lookup();
                var output = new Test();
                var source = "Input.xml";
                var outFile = "Output.xml";

                System.Xml.Xsl.Fakes.ShimXslCompiledTransform.AllInstances.TransformXmlReaderXmlWriter = (xslCompiledTransform, xmlReader, xmlWriter) =>
                {
                    xslCompiledTransform.Transform(xmlReader, map.TransformArgs, xmlWriter);
                };
				
				/* rest of test */
			}

Here we are rerouting the Transform call made by the TestableMapBase class to the appropriate override which takes an XslArgumentList. We capture the argument list for the map we are testing in a closure.

Success!

BizTalkBLog6

3 thoughts on “Unit Testing BizTalk Maps – External functoids

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s