Submit to Reddit      
  

The ChannelAdam Test Framework Library for BizTalk

Overview

The open source ChannelAdam Test Framework Library for BizTalk is an automated testing library for testing BizTalk 2010/2013/2016 artifacts.

Currently it provides you with an enhanced ability to use modern standard industry testing practices to test BizTalk Maps.

Please see the article Modern Testing of BizTalk Maps for background information on the issues with using Microsoft's out-of-the-box functionality for testing Microsoft BizTalk Maps.

This library provides the following benefits and features:

  • Works with BizTalk 2010 / 2013 / 2016
  • Never again set the project property "Enable Unit Testing" to "True"!
  • BizTalk Map unit tests can be executed against a release build
  • The provided map tester classes allow you to easily unit test both XML and flat file BizTalk Maps
  • The design of tester classes follow the clean Arrange-Act-Assert (AAA) testing pattern
  • Support for arranging the input and expected output from a string, XElement, an XML serialisable object or loading from an embedded resource
  • Performs schema validation of both the input and output by default (easily overridable)
  • Support for mocking External Assembly class methods
  • Support for filtering assertions of the expected output to allow for dynamic data (such as timestamps or GUIDs)
  • Detailed information is provided allowing you to see the differences between the expected output and the actual output of the map.

The ChannelAdam Test Framework Libraries are fully compatible with MSTest and .NET Framework 4.0 and above.

Note: I highly recommend using Behaviour-Driven Development practices and the usage of SpecFlow.


Getting Started

NuGet Package Installation

To install the ChannelAdam.TestFramework.BizTalk NuGet package run the following command in the Package Manager Console:

PM> Install-Package ChannelAdam.TestFramework.BizTalk


Show Me Some Code!

Once the NuGet package is installed, you are now ready to jump into the code!

The Basics

There are 4 map tester classes to be used depending on the type of input and output of your map:

  • BizTalkXmlToXmlMapTester - for testing a map that maps from XML to XML
  • BizTalkXmlToFlatFileMapTester - for testing a map that maps from XML to a flat file
  • BizTalkFlatFileToFlatFileMapTester - for testing a map that maps from a flat file to a flat file
  • BizTalkFlatFileToXmlMapTester - for testing a map that maps from a flat file to XML

Below is some sample MSTest test method code (in a very raw form) for testing a map that maps from XML to XML.

// ==================
// Variables
// ==================

// Define some helpful variables for our testing artifacts
static Assembly thisAssembly = this.GetType().Assembly;
static string inputXmlTestDataEmbeddedResourceName          = thisAssembly.GetName().Name + ".InputXmlTestDataEmbeddedResource.xml"
static string expectedOutputXmlTestDataEmbeddedResourceName = thisAssembly.GetName().Name + ".ExpectedOutputXmlTestDataEmbeddedResource.xml"

// Create the MSTest compatible logging assertion class
var logAssert = new ChannelAdam.TestFramework.MSTest.LogAssert();

// Create the map tester - in this case for a map from XML to XML
var xmlToXmlMapTester = new BizTalkXmlToXmlMapTester(logAssert);

// ==================
// Arrange
// ==================

// Arrange the input XML and expected output XML from embedded resources
xmlToXmlMapTester.ArrangeInputXml(thisAssembly, inputXmlTestDataEmbeddedResourceName);
xmlToXmlMapTester.ArrangeExpectedOutputXml(thisAssembly, expectedOutputXmlTestDataEmbeddedResourceName);

// ==================
// Act
// ==================
xmlToXmlMapTester.TestMap(new MySpiffyMapFromXmlToXml());

// ==================
// Assert
// ==================
xmlToXmlMapTester.AssertActualOutputXmlEqualsExpectedOutputXml();

Note: I highly recommend using the MoqTestFixture as a base class to your test class as it removes the need for you to be concerned with instantiating the LogAssert class and provdes a number of base methods to make testing easier. Please see the ChannelAdam Test Framework Libraries documentation for more information.

The test code above results in the following test output.

10/10/16 10:00:00 PM - The input XML for the map is: 
<ns0:Request xmlns:ns0="http://SampleBizTalkMaps.Schemas.XmlRequest">
  <Key>Key_0</Key>
  <StringValue>StringValue_0</StringValue>
  <DateTimeValue>1999-05-31T13:20:00.000-05:00</DateTimeValue>
  <IntegerValue>100</IntegerValue>
  <DecimalValue>10.4</DecimalValue>
</ns0:Request>

10/10/16 10:00:00 PM - The expected output XML of the map is: 
<ns0:Response xmlns:ns0="http://SampleBizTalkMaps.Schemas.XmlResponse">
  <Key>Key_0</Key>
  <StringValue>StringValue_0FAKE_GUID</StringValue>
  <DateTimeValue>1999-05-31T13:20:00.000-05:00</DateTimeValue>
  <IntegerValue>100</IntegerValue>
  <DecimalValue>10.4</DecimalValue>
</ns0:Response>

10/10/16 10:00:00 PM - Validating the input XML for the BizTalk map
10/10/16 10:00:00 PM - Executing the BizTalk map (XML to XML) MySpiffyMapFromXmlToXml
10/10/16 10:00:00 PM - Asserting XML output from the BizTalk map exists is True

10/10/16 10:00:00 PM - BizTalk map (XML to XML) execution completed

10/10/16 10:00:00 PM - The actual output XML from the map is: 
<ns0:Response xmlns:ns0="http://SampleBizTalkMaps.Schemas.XmlResponse">
  <Key>Key_0</Key>
  <StringValue>StringValue_0FAKE_GUID</StringValue>
  <DateTimeValue>1999-05-31T13:20:00.000-05:00</DateTimeValue>
  <IntegerValue>100</IntegerValue>
  <DecimalValue>10.4</DecimalValue>
</ns0:Response>
10/10/16 10:00:00 PM - Validating the output XML from the BizTalk map

10/10/16 10:00:00 PM - Asserting actual and expected XML are equal
10/10/16 10:00:00 PM - Asserting The XML is as expected is True
10/10/16 10:00:00 PM - The XML is as expected

And the test passed successfully!


Advanced Usage

More advanced functionality can be performed with the overloads on the methods used in the above test code.

Arrange - Input and Expected Output

The BizTalkXmlToXmlMapTester has the following method overrides for arranging the input XML.

  • void ArrangeInputXml(Assembly assembly, string resourceName) - to arrange the input XML from an embedded resource in the given assembly

  • void ArrangeInputXml(XElement xmlElement) - to arrange the input XML from a given XElement

  • void ArrangeInputXml(object valueToSerialise) - to arrange the input XML by serialising the given object

  • void ArrangeInputXml(object valueToSerialise, XmlRootAttribute xmlRootAttribute) - to arrange the input XML by serialising the given object and applying the given XmlRootAttribute during the serialisation

  • void ArrangeInputXml(object valueToSerialise, XmlAttributeOverrides xmlAttributeOverrides) - to arrange the input XML by serialising the given object and applying the given XmlAttributeOverrides during the serialisation

  • void ArrangeInputXml(string xmlValue) - to arrange the input XML from the given XML string

Input can be arranged from an embedded resource, XElement, object that is XML serialisable (for which you can also override the root XML namespace when serialised) or simply a string.

This same pattern is followed for arranging the expected output of the map, and for arranging the contents of flat files with the other map tester classes.


Act - Test the Map

The BizTalkXmlToXmlMapTester has the following method overrides for testing your BizTalk Map.

  • void TestMap(TransformBase map)

  • void TestMap(TransformBase map, IEnumerable<XsltExtensionObjectDescriptor> xsltExtensionObjectOverrides) (see below - Mocking External Assemblies)

  • void TestMap(TransformBase map, bool validateInputXml, bool validateOutputXml)

  • void TestMap(TransformBase map, IEnumerable<XsltExtensionObjectDescriptor> xsltExtensionObjectOverrides, bool validateInput, bool validateOutputXml)

The validateInputXml and validateOutputXml parameters are self-explanatory.

Mocking External Assemblies

The xsltExtensionObjectOverrides enumerable is the secret to mocking External Assembly class methods. To mock a class, create an Interface that define the method used in the map, and then create and set up a mock object from the Interface. Then use the following code to create the xsltExtensionObjectOverrides, populated with the mock object.

// Mock the external assembly 'MyExternalAssemblyClassUsedInTheMap' class used in the map ;)
var mocksOfExternalAssemblyClassesUsedInMap = new List<XsltExtensionObjectDescriptor>
{
    new XsltExtensionObjectDescriptor(typeof(MyExternalAssemblyClassUsedInTheMap), myMoqMockForThatClass.Object)
};

And test the map, providing the xsltExtensionObjectOverrides parameter.

// Test the map which will execute methods on the mock objects
xmlToXmlMapTester.TestMap(new MySpiffyMapFromXmlToXml(), mocksOfExternalAssemblyClassesUsedInMap);

For Other examples, including how to mock an External Assembly, please refer to this example BDD feature file and the implementation of the scenario steps.


Assert - Compare the Actual Output Against the Expected Output

The BizTalkXmlToXmlMapTester has the following method overrides for asserting the actual output from the map against the expected output that was arranged.

  • void AssertActualOutputXmlEqualsExpectedOutputXml()

  • AssertActualOutputXmlEqualsExpectedOutputXml(IXmlFilter xmlFilter)

All the comparison and logging and formatting of any differences is done for you. Easy!

Ignoring / Filtering XML Elements

The override allows you to specify an IXmlFilter which allows you to specify a list of the local names of XML elements to ignore in comparisons, and/or a list of XPath expressions to ignore in comparisons.

Two constructor methods on XmlFilter allow you to easily specify these lists.

  • XmlFilter(IList<string> elementLocalNamesToIgnore)

  • XmlFilter(IList<string> elementLocalNamesToIgnore, IList<string> xpathsToIgnore)

Ignoring / Filtering Flat File Differences

Ignoring differences in flat files is a little more tricky, but not too difficult. The ChannelAdam library uses the DiffPlex library for performing text differences.

3 mechanisms on the flat file tester classes are provided for hooking into the DiffPaneModel difference result and manipulating it before a final decision is made as to whether or not the actual output is similar enough to the expected output.

  • An event that is raised with the differences

      /// <summary>
      /// Occurs when a text difference is detected, allowing a listener to filter the differences and
      /// change the Line[x].Type to ChangeType.UnChanged so that the difference is no longer treated as a difference.
      /// </summary>
      /// <remarks>
      /// This event or the TextDifferenceFilter property can be used for this purpose.
      /// </remarks>
      public event EventHandler<TextDifferenceDetectedEventArgs> TextDifferenceDetectedEvent;
    
  • An action delegate method that is called with the differences

      /// <summary>
      /// Gets or sets the Action delegate to be invoked when a text difference is detected, allowing differences to be filtered out by
      /// changing the Line[x].Type to ChangeType.UnChanged - so that a difference is no longer treated as a difference.
      /// </summary>
      /// <value>The Action delegate.</value>
      /// <remarks>
      /// This property or TextDifferenceDetectedEvent can be used for this purpose.
      /// </remarks>
      public Action<DiffPaneModel> TextDifferenceFilter { get; set; }
    
  • A virtual method that allows you to inherit from the tester class and override the differences when they are detected.

      protected virtual void OnTextDifferenceDetected(DiffPaneModel differences)
    


comments powered by Disqus