BizTalk Server 2013: Step-by-Step to implement Unit Testing in Schemas and Maps

Posted: August 28, 2014 in BizTalk
Tags: , , , , ,

To implement BizTalk Server 2013 unit test within Visual Studio 2012 to test Schemas and Map we need to:

  • Open your BizTalk Project in Visual Studio.NET 2012, in this sample: “UnitTestingFeatureWithMaps.sln”
  • In Solution Explorer, right-click in the BizTalk Server project, in this sample “UnitTestingFeatureWithMaps”, and then click Properties.
    • In Project Designer, click the Deployment property page tab and set “Enable Unit Testing” option to “True”.

BizTalk-Server-2013-Project-Designer-Deployment-property

    • Close the project properties page saving the changes.
  • In main menu, click Build, and then click Rebuild Solution.

To create a unit test project

  • On the File menu, choose “Add”, and then choose “New Project….”
  • In the New Project dialog box, expand “Installed”, expand “Visual C#”, and then choose “Test”.
  • From the list of templates, select “Unit Test Project”.

BizTalk-Server-2013-New-Unit-Test-Project

  • In the Name box, enter “UnitTestProject1”, and then choose “OK”.
  • The “UnitTestProject1” project is added to the the “UnitTestingFeatureWithMaps” solution.
  • In the “UnitTestProject1” project, for us to be able to accomplished testing BizTalk Schemas and Maps, we need to manually add the following references to the solution:
    • Microsoft.BizTalk.TestTools – you can find this assembly in the following directory: “C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies\Microsoft.BizTalk.TestTools.dll”
    • Microsoft.XLANGs.BaseTypes – you can find this assembly in the following directory: “C:\Program Files (x86)\Microsoft BizTalk Server 2013\Microsoft.XLANGs.BaseTypes.dll”
    • BizTalk Server project assembly – in this case “UnitTestingFeatureWithMaps”
  • You can accomplish this by:
    • In Solution Explorer, select “References” in the “UnitTestProject1” project and then choose “Add Reference…” from the context menu.

We need two test class, one for testing the Schema and the other to test the map. We can use the UnitTest1.cs that was generated by the project template, but we should give the file and class more descriptive names. We can do that in one step by renaming the file in Solution Explorer.

To unit test a schema you can leveraging the unit test framework in Visual Studio. In the Unit Test Class you can test methods like below for validating XML document instance:

        /// <summary>
        ///A test for PersonOrigin Constructor
        ///</summary>
        [TestMethod()]
        public void PersonOriginConstructorTest()
        {
            PersonOrigin target = new PersonOrigin();

            //=== Schema input file for validation ===//
            string strSourcePO_XML = testContextInstance.TestDir + "..\\..\\..\\Files\\PersonOrigin.xml";

            //=== Validate the XML Input message against the schema ===//
            Assert.IsTrue(target.ValidateInstance(strSourcePO_XML, 
                Microsoft.BizTalk.TestTools.Schema.OutputInstanceType.XML));
        }

To unit test a map you can leveraging the unit test framework in Visual Studio. In the Unit Test Class you can test method like below for testing a mapping:

        [TestMethod()]
        public void HowMapsWorksMapTest()
        {
            /*********************************************************************************
             * There is a bug with Map Unit Test inside Microsoft.BizTalk.TestTools.dll 
             * Microsoft had missed on to upgrade TestableMapBase class. They still using the 
             * BTSXslTransform instead of using XslCompiledTransform witch will cause the 
             * TestMap() function to failed.
             * 
             * The following code was the expected code for BizTalk Map unit testing 
             *********************************************************************************/

            HowMapsWorks map = new HowMapsWorks();

            //=== Use the HelloWorld sample directory path for the message files ===//
            string strSourcePO_XML = testContextInstance.TestDir + "..\\..\\..\\Files\\PersonOrigin.xml";
            string strDestInvoice_XML = testContextInstance.TestDir + "\\OUT\\PersonTarget2.xml";


            //=== Test the map by using the TestMap method of the TestableMapBase class ===//
            map.ValidateOutput = true;
            map.TestMap(strSourcePO_XML,
                           Microsoft.BizTalk.TestTools.Schema.InputInstanceType.Xml,
                           strDestInvoice_XML,
                           Microsoft.BizTalk.TestTools.Schema.OutputInstanceType.XML);


            //=== Output file should be created as a result of testing the map ===//
            Assert.IsTrue(File.Exists(strDestInvoice_XML));
        }

Maps Unit Testing Workaround (until the hotfix is unavailable)

Unfortunately there is a bug in BizTalk Server 2013 with Map Unit Test inside Microsoft.BizTalk.TestTools.dll, so each time we try to run the unit test for the map it give us the following error:

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)

Microsoft had missed on to upgrade TestableMapBase class. They still using the BTSXslTransform instead of using XslCompiledTransform witch will cause the TestMap() function to failed.
You can find a wrapper (provide by  shadabanwer) here: Map Unit test does not work in BizTalk 2013 because TestableMapBase class is not correct. However there is also a problem with schema (input and output) validation options… so I decide to recreated a new custom wrapper based on Microsoft.BizTalk.TestTools.dll and the solution provided by shadabanwer and fixed all the problems because validating the output instance generated by the map is an important step to validate your maps using Unit Testing. You can find this DLL here: BizTalk Server 2013: Wrapper classes to perform Unit Testing in Maps

You must use this workaround until Microsoft fix this bug.

So to be able to successfully test our maps we need to:

        [TestMethod()]
        public void HowMapsWorksMapTest()
        {
            HowMapsWorks map = new HowMapsWorks();

            //=== Map input file instance to be mapped  ===//
            string strSourcePO_XML = testContextInstance.TestDir + "..\\..\\..\\Files\\PersonOrigin.xml";
            //=== Path for the Map output file instance with the result of the transformation  ===//
            string strDestInvoice_XML = testContextInstance.TestDir + "\\Out\\PersonTarget2.xml";

            //WORKAROUND SOLUTION to test maps
            SandroPereira.BizTalk.MapTestTools.TestableMapBase mapper = new SandroPereira.BizTalk.MapTestTools.TestableMapBase();

            mapper.Mapper = map;
            mapper.Mapper.ValidateOutput = true;

            //=== Test the map by using the TestMap method of a custom TestableMapBase class ===//
            //=== that uses the XslCompiledTransform. This class is basically an improved    ===//
            //=== clone of the class present in the Microsoft.BizTalk.TestTools DLL          ===//
            mapper.TestMap(strSourcePO_XML,
                Microsoft.BizTalk.TestTools.Schema.InputInstanceType.Xml,
                strDestInvoice_XML,
                Microsoft.BizTalk.TestTools.Schema.OutputInstanceType.XML);

            //=== Output file should be created as a result of testing the map ===//
            Assert.IsTrue(File.Exists(strDestInvoice_XML));
        }

Check the full detailed article here: BizTalk Server 2013: Step-by-Step to implement Unit Testing in Schemas and Maps

BizTalk Server 2013: Using the Unit Testing Feature with Schemas and Maps (500.3 KB)
Microsoft | TechNet Gallery

Comments
  1. pauljamesnichols says:

    Hi

    I’m noticing a lot of confusion about testing maps with BT 2013. I just reference a testsettings file which forces the tests to run in 32-bit mode and everything seems fine.
    What do you think to this approach?

    • Never tryied I need to check. Can you send me this testsettings file?

    • Hi Paul,

      Supposedly Test Settings configurations file:
      – Visual Studio 2010 – it was specified in a .testsettings file and it had GUI interaction and xml templating for customization. This can still be used in VS2012 via a ForcedLegacyMode element in the xml, however this is noted as a performance inhibitor for the test runner.
      •- Visual Studio 2012 – It’s now specified in a .runsettings file, with no GUI config editting, IntelliSense support or item creation templating.

      If we add a .runsettings file to our test project it doesn’t work!

      However if we take the .testsettings file that was generated for example in Visual Studio 2010/BizTalk Server 2010 and add this file to our Test project in Visual Studio 2012/BizTalk Server 2013 it works and solve the problem, don’t know why but is other workaround to solve this problem.

  2. pauljamesnichols says:

    sorry didn’t mean to post twice i was on the train and i thought the first comment failed!

  3. tr0users says:

    My experience is the same as Sandro’s. Any news on when Microsoft will have a hotfix out for this?

  4. tr0users says:

    I “think” there may also be a problem when testing maps to flat files in BTS2013r2. I outlined here: http://stackoverflow.com/questions/27704034/testable-map-base-xml-to-flat-file-fails-in-bts2013r2.

  5. Hello Sandro, I was trying to test a schema that imports other schemas with the validate method but it fails with any valid document (the documents were validated in Visual Studio). Have you ever had the same problem?

    • Hi Ricardo,

      Unfortunately never tested this scenario… I need to try if it works or not

      • Paul Nichols says:

        I’ve no idea if this helps butthis is the code I recently wrote to ensure when we unit test a map it can be run in an environment with no artefacts in the GAC.

        The built in validation of a map seemed to fail when the schemas assemblies weren’t in the GAC.

        A build server should have no artefacts in the GAC so I’ve been trying a few things like this and MS Fakes recently to help us run our tests on a build server.

        All references do need to be in the Bin folder but they tend to be there when you have a reference to the map you’re testing

        Calling code:

        BizTalkMapTestHelper.TestMap(map, inputFileName);

        Helper class:

        ///

        /// A collection of methods to help with testing maps.

        ///

        public static class BizTalkMapTestHelper

        {

        public static string TestMap(TestableMapBase map, string inputFilename)

        {

        ValidateInput(map, inputFilename);

        var destinationFilename = Path.GetTempFileName();

        map.ValidateInput = false;

        map.ValidateOutput = false;

        map.TestMap(inputFilename, InputInstanceType.Xml, destinationFilename, OutputInstanceType.XML);

        ValidateOutput(map, destinationFilename);

        return destinationFilename;

        }

        private static void ValidateInput(TestableMapBase map, string fileNameOfXmlToValidate)

        {

        var schemas = map.SourceSchemas;

        var attrs = GetSchemaReferenceAttributes(map);

        ValidateInstance(fileNameOfXmlToValidate, schemas, attrs);

        }

        private static void ValidateOutput(TestableMapBase map, string fileNameOfXmlToValidate)

        {

        var schemas = map.TargetSchemas;

        var attrs = GetSchemaReferenceAttributes(map);

        ValidateInstance(fileNameOfXmlToValidate, schemas, attrs);

        }

        private static IEnumerable GetSchemaReferenceAttributes(TestableMapBase map)

        {

        var attrs = System.Attribute.GetCustomAttributes(map.GetType(), typeof(Microsoft.XLANGs.BaseTypes.SchemaReferenceAttribute)).Cast();

        return attrs;

        }

        private static void ValidateInstance(string fileNameOfXmlToValidate, IEnumerable schemas, IEnumerable attrs)

        {

        string validationErrors = string.Empty;

        XmlSchemaSet schemaSet = new XmlSchemaSet();

        foreach (var schema in schemas)

        {

        schemaSet.Add(LoadSchema(schema, attrs));

        }

        XDocument doc = XDocument.Load(fileNameOfXmlToValidate);

        doc.Validate(schemaSet, (o, e) => { validationErrors += e.Message + Environment.NewLine; });

        if (validationErrors.Any())

        {

        throw new XmlSchemaValidationException(validationErrors);

        }

        }

        private static XmlSchema LoadSchema(string schemaName, IEnumerable attrs)

        {

        Type schemasType = attrs.Single(a => a.Reference == schemaName).Type;

        var schemaBase = Activator.CreateInstance(schemasType) as Microsoft.XLANGs.BaseTypes.SchemaBase;

        if (schemaBase == null)

        {

        throw new Exception(“Could not cast to a SchemaBase”);

        }

        return schemaBase.Schema;

        }

        }

  6. Sandeep says:

    Sandro /Ricard have you tried testing schemas which imports/includes other schemas?

  7. I ran into an issue involving custom script functoids calling .Net components. The current Sandro fix omits the addition of extension objects. By creating a fake assembly (see https://truenorthit.co.uk/2014/11/17/unit-testing-biztalk-maps-external-functoids/) I got it working. Code looks like this:

    //use shims to change the compiled transform behavior to accept xslt arguments
    using (ShimsContext.Create())
    {
    System.Xml.Xsl.Fakes.ShimXslCompiledTransform.AllInstances.TransformXmlReaderXmlWriter = (xslCompiledTransform, xmlReader, xmlWriter) =>
    {
    xslCompiledTransform.Transform(xmlReader, map.Mapper.TransformArgs, xmlWriter);
    };

    //execute the map
    map.Mapper.ValidateInput = testcase.validateinput;
    map.Mapper.ValidateOutput = testcase.validateoutput;
    var inputinstancetype = (Microsoft.BizTalk.TestTools.Schema.InputInstanceType)testcase.inputinstancetype;
    var outputinstancetype = (Microsoft.BizTalk.TestTools.Schema.OutputInstanceType)testcase.outputinstancetype;
    string inputinstancefilelocation = System.IO.Path.Combine(BASEPATH, subfolder, string.Concat(mapname, “_”, testcase.name, “_input.” + testcase.inputinstancetype.ToString().ToLower()));
    string outputinstancefilelocation = System.IO.Path.Combine(BASEPATH, subfolder, string.Concat(mapname, “_”, testcase.name, “_output.” + testcase.outputinstancetype.ToString().ToLower()));

    try
    {
    map.TestMap(inputinstancefilelocation, inputinstancetype, outputinstancefilelocation, outputinstancetype);
    }
    catch (Microsoft.BizTalk.TestTools.BizTalkTestAssertFailException btafe)
    {
    Assert.Fail(“Mapping ” + mapname + ” failed in testcase ” + testcase.name + “.” + Environment.NewLine + btafe.ToString());
    }

    etc..
    }

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s