Announcing the ChannelAdam Nancy SOAP Library

My last article introduced the ChannelAdam SOAP Library to make it as easy as possible to create a SOAP 1.1 or SOAP 1.2 payload.

Also as mentioned in that last article, the .NET library Nancy is an excellent lightweight web framework for building HTTP-based web services. While Nancy will not itself ship support for SOAP it still can be used for hosting SOAP web services, if you can create the SOAP payload - and that is exactly what the ChannelAdam SOAP Library helps you do.

However, even after you use the ChannelAdam SOAP Library to build your SOAP payloads, there are still a few hurdles to jump over in order configure Nancy to work with SOAP HTTP requests… until now!

Announcing the release of the open source ChannelAdam Nancy SOAP Library which provides an adapter that makes it as easy as possible to work with SOAP 1.1 and SOAP 1.2 payloads in Nancy.

For information on how to get started, please see the usage documentation.

Enjoy!

Announcing the ChannelAdam SOAP Library

The Simple Object Access Protocol (SOAP) has been around for more than a decade. While HTTP-based API’s and RESTful services are the current trend, SOAP web services are still the rock bed of most businesses and contemporary systems.

On-premises API gateways such as the CA API Gateway has had support for publishing SOAP web services for quite a while.

In fact, there is still such demand in the industry for integration with SOAP services that Microsoft’s newer cloud-based Azure API Management service also now has the capability to publish SOAP API’s.

The .NET library Nancy is an excellent lightweight web framework for building HTTP-based web services. While Nancy will not itself ship support for SOAP it still can be used for hosting SOAP web services, if you can create the SOAP payload.

Libraries such as Nancy or Mock4Net (a tiny mock HTTP server for .NET) can be used in automated Unit Tests to dynamically create mock web services that return the exact response required for each specific test case. In fact, I personally use this specific technique to perform advanced Unit Testing of black-box code artefacts such as BizTalk orchestrations.

However, creating and composing a SOAP envelope, header, and body or fault has always been a tricky undertaking, and unless you have studied the SOAP specifications in detail and use them daily, it is easy to either forget or confuse the differences between SOAP 1.1 and SOAP 1.2 - that is, until now!

Introducing the ChannelAdam SOAP Library – a fluent .NET API to make it as easy as possible to create a SOAP 1.1 or SOAP 1.2 payload.

For information on how to get started, please see the usage documentation.

You might also be interested in the ChannelAdam Nancy SOAP Library which makes using SOAP with Nancy even easier.

The ChannelAdam Test Framework Libraries

IMPORTANT: DEPRECATION NOTICE

This documention is for Version 1 of these full .NET Framework libraries and it has been deprecated.

Please see the [Version 2] documentation for the following ChannelAdam .NET Standard libraries.

Overview

The open source ChannelAdam Test Framework Libraries are a set of automated testing libraries to make testing your .NET code easier.

There are currently 3 main libraries:

  • The ChannelAdam Test Framework - the common test framework library without a dependency on any other test runner or library
  • The ChannelAdam Test Framework for MSTest - containing MSTest-specific implementations of interfaces in the common library
  • The ChannelAdam Test Framework for BizTalk - containing functionality specifically for testing Microsoft BizTalk artifacts.

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

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

Getting Started

NuGet Package Installation

The easiest way to get started is to install the library that suits your favourite test runner or test library. Currently however there is only the MSTest implementation…

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

1PM> Install-Package ChannelAdam.TestFramework.MSTest

Features

Logger

Logging useful information to the output of a test is extremely important.

The ISimpleLogger interface in the ChannelAdam Core Library provides a very simple interface with a simple Log method that is intended for usage in scenarios such writing test output.

The SimpleConsoleLogger class implements ISimpleLogger and is a thin wrapper around Console.WriteLine().

Log and then Assert

I cannot emphasise enough how important it is to log useful to the output of a test. Developers and analysts almost should be able to read the output of a test like a story. Analysts especially should review the test outputs and be satisfied that the test was implemented correctly - almost as if they performed the test themselves. Therefore, when an assertion is performed, we want the test output to actually tell us that an assertion is taking place and provide a information about what is being asserted.

The Log Asserter interface provides all the usual assertion methods that you are familiar with, and the first parameter of all of the methods is a string that will be outputted to an ISimpleLogger (provided in the constructor) before performing the actual assertion.

This LogAssert class is the MSTest implementation of the Log Asserter interface.

Test Easy Base Class for Tests

Logging, assertions and testing for exceptions are 3 of some fundamental features common to most automated test suites. Wouldn’t it be nice to be able to perform that functionality easily and keep your test code to a minimum?

The TestEasy base class for tests provides those abilities.

This class provides the following helpful properties.

  • ISimpleLogger Logger - for logging to the test output

  • ILogAsserter LogAssert - your handy-dandy log asserter

  • ExpectedExceptionDescriptor ExpectedException - describes the exception that is expected to occur for a specific test case

  • Exception ActualException - for storing the actual exception that occurred

When the ActualException property is set, the details are automatically logged to the test output.

TestEasy also contains the following helpful methods:

  • void Try(Action action) - performs the given action and catches exceptions into ActualException

  • void Try(Func<Task> action) - performs the given asynchronous action that returns a Task, waits for it finish, and catches exceptions into ActualException

  • void AssertNoExceptionOccurred() - asserts that no ActualException exists/occurred

  • void AssertExpectedException(Type expectedExceptionType) - asserts that type of the value of ActualException is the given expected type

  • void AssertExpectedException() - asserts that the ActualException is equivalent to that described by the ExpectedException descriptor. When the ActualException is an System.AggregateException, the assertion will pass if there is at least one inner exception equivalent to the ExpectedException descriptor.

Expected Exception Descriptor

The ExpectedException property is implemented by the ExpectedExceptionDescriptor class. This class describes the exception that is expected to occur.

There are 2 properties in ExpectedExceptionDescriptor that are specifically used in the AssertExpectedException() method:

  • string MessageShouldContainText - which describes the substring that the ActualException.Message property should contain

  • Type ExpectedType - which describes the System.Type that ActualException should be.

Either or both of these properties can be set. When set, the details are automatically logged to the test output.

Moq Test Fixture Base Class for Tests

MoqTestFixture inherits all the goodies from TestEasy and also provides a MyMockRepositoy property to access the Moq MockRepository.

The default constructor automatically creates a Moq “loose” MockRepository, but that can be specified in an overload of the constructor.

Text Tester

The TextTester class is used in tests for asserting whether actual text is similar enough to the given expected text.

Under the covers, it uses the DiffPlex open source library to identify differences.

The TextTester class has the following properties:

  • string ActualText

  • string ExpectedText

  • DiffPaneModel Differences

  • Action<DiffPaneModel> TextDifferenceFilter - a filter for the diff (see below)

And the following methods:

  • void ArrangeActualText(Assembly assembly, string resourceName) - to arrange the actual text from an embedded resource in the given assembly

  • void ArrangeActualText(string text) - to arrange the actual text from the given string

  • void ArrangeExpectedText(Assembly assembly, string resourceName) - to arrange the expected text from an embedded resource in the given assembly

  • void ArrangeExpectedText(string text) - to arrange the expected text from the given string

  • virtual void AssertActualTextEqualsExpectedText() - to assert the actual text against the expected text

  • bool IsEqual() - determines if the given actual and expected text is equivalent

  • virtual bool IsEqual(string expected, string actual) - determines if the given actual and expected text is equivalent

Filtering Text Differences

The TextTester class provides 3 mechanisms for hooking into the DiffPlex DiffPaneModel difference result, allowing you to manipulate it before a final decision is made as
to whether or not the actual text is similar enough to the expected text.

  • An event that is raised with the differences
1    /// <summary>
2    /// Occurs when a text difference is detected, allowing a listener to filter the differences and
3    /// change the Line[x].Type to ChangeType.UnChanged so that the difference is no longer treated as a difference.
4    /// </summary>
5    /// <remarks>
6    /// This event or the TextDifferenceFilter property can be used for this purpose.
7    /// </remarks>
8    public event EventHandler<TextDifferenceDetectedEventArgs> TextDifferenceDetectedEvent;
  • An action delegate method that is called with the differences
1    /// <summary>
2    /// Gets or sets the Action delegate to be invoked when a text difference is detected, allowing differences to be filtered out by
3    /// changing the Line[x].Type to ChangeType.UnChanged - so that a difference is no longer treated as a difference.
4    /// </summary>
5    /// <value>The Action delegate.</value>
6    /// <remarks>
7    /// This property or TextDifferenceDetectedEvent can be used for this purpose.
8    /// </remarks>
9    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.
1    protected virtual void OnTextDifferenceDetected(DiffPaneModel differences)

For a quick example, please see the Behaviour Specification for the TextTester class and the associated
text tester code

XML Tester

The XmlTester class is used in tests for asserting whether the actual XML is similar enough to the given expected XML.

Under the covers, it uses the XMLUnit.NET open source library to identify differences.

The XMLTester class has the following properties:

  • XElement ActualXml

  • XElement ExpectedXml

  • Diff Differences

And the following methods:

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

  • void ArrangeActualXml(XElement xmlElement) - to arrange the actual XML from a given XElement

  • void ArrangeActualXml(object valueToSerialise) - to arrange the actual XML by serialising the given object

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

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

  • void ArrangeActualXml(string xmlValue) - to arrange the actual XML from the given XML string

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

  • void ArrangeExpectedXml(XElement xmlElement) - to arrange the expected XML from a given XElement

  • void ArrangeExpectedXml(object valueToSerialise) - to arrange the expected XML by serialising the given object

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

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

  • void ArrangeExpectedXml(string xmlValue) - to arrange the expected XML from the given XML string

  • virtual void AssertActualXmlEqualsExpectedXml() - to assert the actual XML against the expected XML

  • virtual void AssertActualXmlEqualsExpectedXml(IXmlFilter xmlFilter) - to assert the actual XML against the expected XML, ignoring items listed in the given IXmlFilter (see below)

  • bool IsEqual() - determines if the given actual and expected XML is equivalent

  • bool IsEqual(XNode expected, XNode actual) - determines if the given actual and expected XNode XML is equivalent

  • virtual bool IsEqual(XmlNode expected, XmlNode actual) - determines if the given actual and expected XmlNode XML is equivalent

Ignoring / Filtering XML Elements

The AssertActualXmlEqualsExpectedXml(IXmlFilter xmlFilter) override allows you to specify an IXmlFilter interface 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.

Examples

For a quick example, please see the Behaviour Specification for the XmlTester class and the XML tester code

XML Filter

The XmlFilter class 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 allow you to easily specify these lists.

  • XmlFilter(IList<string> elementLocalNamesToIgnore)

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

XML Asserter

The XmlAsserter class provides utility methods for performing assertions on XML values. It has the following methods:

  • void XPathValueEquals(string description, string xpath, XNode rootElement, string expected) - to assert that the given XPath of the provided XNode has the given expected string value

  • void XPathValueEquals(string description, string xpath, XNode rootElement, XmlNamespaceManager namespaceManager, string expected) - uses the provided namespace manager to assert that the given XPath of the provided XNode has the given expected string value

  • void XPathValuesAreEqual(string description, string xpath, XNode expectedElements, XNode actualElements) - to assert that the value of the XPath in the given actual XNode equals the corresponding value in the given expected XNode

  • void XPathValuesAreEqual(string description, string expectedXpath, XNode expectedElements, string actualXpath, XNode actualElements) - to assert that the value of the actual XPath in the actual XNode is the same as the value of the expected XPath in the expected XNode

  • void XPathValuesAreEqual(string description, string expectedXpath, XNode expectedElements, XmlNamespaceManager expectedNamespaceManager, string actualXpath, XNode actualElements, XmlNamespaceManager actualNamespaceManager) - to assert that the value of the actual XPath in the actual XNode is the same as the value of the expected XPath in the expected XNode, using the provided XML namespace managers

  • void AreEqual(XElement expectedXml, XElement actualXml) - to assert that the given expected XElement is equivalent to the actual XElement

  • void AreEqual(XElement expectedXml, XElement actualXml, IXmlFilter xmlFilter) - to assert that the given expected XElement is equivalent to the actual XElement - using the given IXmlFilter

Mapping Testers

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

The MappingFromXmlToXmlTester class 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

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

  • void ArrangeExpectedOutputXml(XElement xmlElement) - to arrange the expected output XML from a given XElement

  • void ArrangeExpectedOutputXml(object valueToSerialise) - to arrange the expected output XML by serialising the given object

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

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

  • void ArrangeExpectedOutputXml(string xmlValue) - to arrange the expected output 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.

Assert - Compare the Actual Output Against the Expected Output

The MappingFromXmlToXmlTester class 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) (see below)

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

Ignoring / Filtering XML Elements

The AssertActualOutputXmlEqualsExpectedOutputXml(IXmlFilter xmlFilter) 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
1    /// <summary>
2    /// Occurs when a text difference is detected, allowing a listener to filter the differences and
3    /// change the Line[x].Type to ChangeType.UnChanged so that the difference is no longer treated as a difference.
4    /// </summary>
5    /// <remarks>
6    /// This event or the TextDifferenceFilter property can be used for this purpose.
7    /// </remarks>
8    public event EventHandler<TextDifferenceDetectedEventArgs> TextDifferenceDetectedEvent;
  • An action delegate method that is called with the differences
1    /// <summary>
2    /// Gets or sets the Action delegate to be invoked when a text difference is detected, allowing differences to be filtered out by
3    /// changing the Line[x].Type to ChangeType.UnChanged - so that a difference is no longer treated as a difference.
4    /// </summary>
5    /// <value>The Action delegate.</value>
6    /// <remarks>
7    /// This property or TextDifferenceDetectedEvent can be used for this purpose.
8    /// </remarks>
9    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.
1    protected virtual void OnTextDifferenceDetected(DiffPaneModel differences)

Modern Testing of BizTalk Maps

Overview

In today’s modern world of software development with continuous integration as a standard industry practice, what do you think would happen if you told a developer that they need to set a special Visual Studio project property in their build configuration in order for their code to “support unit testing”?

They would laugh at you in disbelief! Why does code need to be built in a special way in order to be testable?! This laughing typically would be followed by heart-ache, maybe a little quiet sobbing and an uncontrollable eye twitch as they realise that decade-old standard industry software development practices are crashing down all around them and they start reconsidering their life choices.

Sadly, the only thing that you can do is agree and try to console them by saying it is “just because it’s [technology xyz]".

This is the unfortunate world for a typical BizTalk developer using the out-of-the-box functionality.

And this embarrassing situation has other negative implications:

  • As described in Unit Testing with BizTalk Server Projects and Using the Unit Testing Feature with Schemas and Maps, BizTalk developers must set the Visual Studio project property “Enable Unit Testing” (under the Deployment tab) to “True”. This magically changes the base class of each BizTalk Map to “Microsoft.BizTalk.TestTools.Mapper.TestableMapBase” in order to “support unit testing”. Yes, an artefact’s base class is actually changed for that build – so what is being tested is not entirely the actual code that will be deployed into production. The difference between a Debug build and a Release build becomes more significant. (Admittedly for a BizTalk Map, this is less of a concern since the actual mapping is performed via XSLT under the covers and not directly by the base class, but the point is valid nevertheless – this approach is taken for other types of artefacts as well, and you really do not want to deploy Debug builds of any of those into a production environment…)

  • If you are like me and some teams I work with, you may want to develop and test the code off-premises (perhaps in your favourite cloud) and then deploy the release/deployment package to an on-premises test environment – where we also want to execute all the tests and have the test results recorded in the on-premises test and release management tools for all team members to access. However, a release/deployment package should never have that special project property set – and this prevents unit tests from compiling and executing against it.

  • When developing a BizTalk Map that uses a Scripting Functoid configured to execute a specified method of a class in an ‘External Assembly’, then “any assembly that you choose a script from … must also be in the global assembly cache (GAC) for Test Map to function correctly”. You need to GAC your external assemblies before running the unit tests. GAC —> Yuck!

Microsoft’s Integration Roadmap clearly shows a measured commitment to and place for BizTalk in the future, alongside their Azure Platform as a Service (PaaS) technologies. With the pending release of BizTalk 2016 as a drop-in replacement for BizTalk 2013 R2 (with no code recompilation required!), I thought it was time to refresh the practice of unit testing BizTalk Maps.

The Solution

The open source ChannelAdam Test Framework Library for BizTalk addresses all of these issues and 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.

With this library you now have the tools needed to easily unit test you BizTalk Maps using modern testing practices. No excuses!

For information on how to get started, please see the usage documentation.

The ChannelAdam Test Framework Library for BizTalk

Overview

This is an open source .NET Framework 4.0, 4.5, 4.6 library providing functionality for the automated testing of BizTalk 2010/2013/2016 artifacts.

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

This library is compatible with the legacy, full .NET Framework 4.0 ChannelAdam Test Framework Libraries (version 1) only.

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 (v1) and .NET Framework 4.0, 4.5, 4.6.

Note: I highly recommend using Behaviour-Driven Development practices and the usage of SpecFlow in conjunction with this library.

Getting Started

NuGet Package Installation

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

1PM> Install-Package ChannelAdam.TestFramework.BizTalk

Usage

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.

 1// ==================
 2// Variables
 3// ==================
 4
 5// Define some helpful variables for our testing artifacts
 6static Assembly thisAssembly = this.GetType().Assembly;
 7static string inputXmlTestDataEmbeddedResourceName          = thisAssembly.GetName().Name + ".InputXmlTestDataEmbeddedResource.xml"
 8static string expectedOutputXmlTestDataEmbeddedResourceName = thisAssembly.GetName().Name + ".ExpectedOutputXmlTestDataEmbeddedResource.xml"
 9
10// Create the MSTest compatible logging assertion class
11var logAssert = new ChannelAdam.TestFramework.MSTest.LogAssert();
12
13// Create the map tester - in this case for a map from XML to XML
14var xmlToXmlMapTester = new BizTalkXmlToXmlMapTester(logAssert);
15
16// ==================
17// Arrange
18// ==================
19
20// Arrange the input XML and expected output XML from embedded resources
21xmlToXmlMapTester.ArrangeInputXml(thisAssembly, inputXmlTestDataEmbeddedResourceName);
22xmlToXmlMapTester.ArrangeExpectedOutputXml(thisAssembly, expectedOutputXmlTestDataEmbeddedResourceName);
23
24// ==================
25// Act
26// ==================
27xmlToXmlMapTester.TestMap(new MySpiffyMapFromXmlToXml());
28
29// ==================
30// Assert
31// ==================
32xmlToXmlMapTester.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.

 110/10/16 10:00:00 PM - The input XML for the map is: 
 2<ns0:Request xmlns:ns0="http://SampleBizTalkMaps.Schemas.XmlRequest">
 3  <Key>Key_0</Key>
 4  <StringValue>StringValue_0</StringValue>
 5  <DateTimeValue>1999-05-31T13:20:00.000-05:00</DateTimeValue>
 6  <IntegerValue>100</IntegerValue>
 7  <DecimalValue>10.4</DecimalValue>
 8</ns0:Request>
 9
1010/10/16 10:00:00 PM - The expected output XML of the map is: 
11<ns0:Response xmlns:ns0="http://SampleBizTalkMaps.Schemas.XmlResponse">
12  <Key>Key_0</Key>
13  <StringValue>StringValue_0FAKE_GUID</StringValue>
14  <DateTimeValue>1999-05-31T13:20:00.000-05:00</DateTimeValue>
15  <IntegerValue>100</IntegerValue>
16  <DecimalValue>10.4</DecimalValue>
17</ns0:Response>
18
1910/10/16 10:00:00 PM - Validating the input XML for the BizTalk map
2010/10/16 10:00:00 PM - Executing the BizTalk map (XML to XML) MySpiffyMapFromXmlToXml
2110/10/16 10:00:00 PM - Asserting XML output from the BizTalk map exists is True
22
2310/10/16 10:00:00 PM - BizTalk map (XML to XML) execution completed
24
2510/10/16 10:00:00 PM - The actual output XML from the map is: 
26<ns0:Response xmlns:ns0="http://SampleBizTalkMaps.Schemas.XmlResponse">
27  <Key>Key_0</Key>
28  <StringValue>StringValue_0FAKE_GUID</StringValue>
29  <DateTimeValue>1999-05-31T13:20:00.000-05:00</DateTimeValue>
30  <IntegerValue>100</IntegerValue>
31  <DecimalValue>10.4</DecimalValue>
32</ns0:Response>
3310/10/16 10:00:00 PM - Validating the output XML from the BizTalk map
34
3510/10/16 10:00:00 PM - Asserting actual and expected XML are equal
3610/10/16 10:00:00 PM - Asserting The XML is as expected is True
3710/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.

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

And test the map, providing the xsltExtensionObjectOverrides parameter.

1// Test the map which will execute methods on the mock objects
2xmlToXmlMapTester.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
1    /// <summary>
2    /// Occurs when a text difference is detected, allowing a listener to filter the differences and
3    /// change the Line[x].Type to ChangeType.UnChanged so that the difference is no longer treated as a difference.
4    /// </summary>
5    /// <remarks>
6    /// This event or the TextDifferenceFilter property can be used for this purpose.
7    /// </remarks>
8    public event EventHandler<TextDifferenceDetectedEventArgs> TextDifferenceDetectedEvent;
  • An action delegate method that is called with the differences
1    /// <summary>
2    /// Gets or sets the Action delegate to be invoked when a text difference is detected, allowing differences to be filtered out by
3    /// changing the Line[x].Type to ChangeType.UnChanged - so that a difference is no longer treated as a difference.
4    /// </summary>
5    /// <value>The Action delegate.</value>
6    /// <remarks>
7    /// This property or TextDifferenceDetectedEvent can be used for this purpose.
8    /// </remarks>
9    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.
1    protected virtual void OnTextDifferenceDetected(DiffPaneModel differences)

About End-To-End Behaviours

« Table of Contents »
Previous « Meeting Milestone 6 — Test Types

The Behaviour Specification Handbook

The Behaviour Specification Writing Process

About End-To-End Behaviours

In addition to specifying the behaviours for an individual component, there also should be only a small number of behaviours (please remember the Testing Pyramid!) specified for end-to-end functionality.

I suggest specifying these end-to-end behaviours in their own feature file, separate from the feature file for each component, since these behaviours span multiple components. I also recommend adding the tag “@E2ETest” at the top of the feature file.


Next » About Outsourcing Delivery

About Outsourcing Delivery

« Table of Contents »
Previous « About End-To-End Behaviours

The Behaviour Specification Handbook

The Behaviour Specification Writing Process

About Outsourcing Delivery

What would happen in the situation where a business project team outsources the analysis, development and testing effort?

First things first, the requirements and scope should be formalised with a signed business contract between the business and the outsourced delivery team.

In the case of having an outsourced delivery team, working on-site or off-site, the business project team is unlikely to be the appropriate team to be running the Behaviour Specification writing session, because they are unlikely to have all the necessary resources, nor a holistic perspective of the solution.

A better approach is to have the outsourced delivery team own and organise workshops to collaborate with the business and other team members in order to perform the business analysis and Behaviour Specification writing sessions. With the outsourced delivery team producing the Behaviour Specifications, they should handover back to the business the completed Behaviour Specifications as part of their documentation and test assets. The Behaviour Specifications and software components also can be used as a metric to track the progress of the delivery team.

In any case, with the requirements and scope already formalised, there is no need to also formalise the Behaviour Specifications with a signed business contract. Please see What Are Behaviour Specifications? for more information on what a Behaviour Specification is, and importantly, is not.


Next Chapter » General Tips

Avoid "I"

« Table of Contents »
Previous « Work Backwards: Then-When-Given

The Behaviour Specification Handbook

Effective Behaviour Specification Steps

Avoid “I”

A quick search for example Behaviour Specifications predominantly reveals behaviours that use the word “I”. If you ever come across this, please ask the following question:

In the context of that software component, who really is ‘I’ and are they important?

As discussed further below, a Behaviour Specification that uses the word “I” is generally not focused or specific enough on the actual behaviour and introduces potential ambiguity and confusion. Just avoid the word “I”!

There are a few reasons why the usage of the word “I” could be so common:

  • Due to having a mindset of “testing” versus “specification”;
  • Due to having a prevalent focus on specifying User Interface or End-to-End behaviours; and
  • Due to copying the style of User Stories.

Due To A Mindset Of Testing

For instance, the Agile Alliance website has a description of the Given-When-Then syntax, and their example uses the word “I”. The same page interestingly states that the Given-When-Then syntax is “intended to guide the writing of acceptance tests for a User Story”. By using the word “test” in their description of the Given-When-Then syntax, their mindset is clearly still focused on testing — rather than specification.

Acceptance Tests pre-date Behaviour-Driven Development, with BDD being an evolution of previous methods, focusing more on “specifying behaviour” rather than talking about “tests”. There is a powerful and yet subtle conceptual difference in perspective between “writing tests” and “specifying behaviour”.

This is one reason why my recommendations differ, as my guidelines come from a mindset focused on specifications — not just testing.

Due To A Focus On User Interfaces

Another possible reason for the common usage of the word “I” is the significant amount of User Interface-focused behaviour examples, where there is an actor interacting with the system.

The following “When” step illustrates a typical, inadvisable, easily found example of a User Interface-focused step.

1When I click on the Submit button
2Then the transaction details are sent to the service that records the transaction

One reason why I recommend against using the word “I” is because it subtly focuses attention on the actor’s actions and focuses less on the behaviour of the component being specified. In the above example, in the English language, the subject of the phrase is “I”. The primary focus is thus needlessly on the actor performing the action of clicking, rather than the subject being the more important aspect that the button’s click behaviour was triggered.

Another reason to avoid “I” is because it begs many other questions to be asked, such as:

  • Who actually is “I”? Is “I” a guest user to the system, or my logged-in user profile?
  • What would be the behaviour if someone else performed the action? For instance, what happens if John does it? How should the component behave then? That information unfortunately is not specified and is therefore ambiguous.

If the purpose of the Behaviour Specification was however to define the behaviour that occurs when a user profile with a specific security role or permission performs the action, then at least the logical name of the security role should be specified, and the word “I” still is unnecessary and not beneficial.

Let us now take this example to the next level – and remove the word “I”.

1When the Submit button is clicked
2Then the transaction details are sent to the service that records the transaction

The wording above is much better focused on the action of clicking the Submit button, without the distraction and unimportant details (in this case) of who clicks the button.

We can do even better than this though! Remembering that the software component context is key to the understanding of Behaviour Specifications, and understanding that this particular behaviour is written in and from the context of a User Interface component, we can rewrite the “When” step as follows.

1When a trigger occurs to submit the transaction details
2Then the transaction details are sent to the service that records the transaction

Within the context of a User Interface component and feature file, the information about a “Submit button” and “clicking” is unnecessary implementation detail. With the exception of specifying security rules, it does not matter who clicks the button, what type of button it is, whether it is a button or any other type of User Interface widget, and whether the widget is mouse clicked, tapped or keyboard, voice or thought activated. What is important for this behaviour however, is that something happened to trigger the recording of transaction details and that there was an attempt to invoke an appropriate business service.

Now that I have just opened the door to further questions about User Interface specifications, please see Have Different Feature Files For Different User Interfaces for details.

Due To Copying The Style Of User Stories

A User Story is a precise and short expression of a requirement by a particular type of business role. As such, the syntax begins by specifying the role of the actor — for instance, “As an accounts manager” — and the rest of the story is written and understood from within the context and perspective of an actor with that specified role. Additionally, a User Story can encompass the functionality provided by multiple software components and systems.

In contrast, a Behaviour Specification is a specification about part of one software component, and that behaviour may apply to many User Stories. Within the context and perspective of that software component, if the word “I” was used, it should only refer to the software component itself. The problem is that it really does not make sense to refer to a software component in that way in the first place.

User Stories and Behaviour Specifications have different purposes. Confusion ensues if one incorrectly mixes the concepts and tries to think about and understand a Behaviour Specification from within the context of a specified role of a User Story.

For the sake of simplicity, and to reduce potential confusion and ambiguity, avoid using the word “I” in the Behaviour Specifications.


Next » Terminology Guidelines

Avoid Generic Verbs

« Table of Contents »
Previous « Use Present Tense

The Behaviour Specification Handbook

General Tips

Avoid Generic Verbs

Avoid the word “handle” or any other verb that is generic and hides or obfuscates the actual behaviour.

For instance, let us pretend that someone wrote the following inadvisable description:

1    Should handle the situation when the total amount is not equal to the sum of all item amounts

The above description has both positive and negative points. On the positive side, the “when” section is descriptive and is an easily understandable, differentiating condition or entry criteria. On the negative side however, “handle” is not actually describing the behaviour that occurs in that situation. What instead is actually meant by “handle”?

A better description is more specific about the behaviour. For instance:

1    Should provide a notification of an error when the total amount is not equal to the sum of all item amounts

This much more clearly describes the actual type of behaviour to be performed.


Next » Write In Everyday Business Language

Avoid Specifying Internal Technical Implementation Details

« Table of Contents »
Previous « Software Component Context Is King

The Behaviour Specification Handbook

General Tips

Avoid Specifying Internal Technical Implementation Details

There is no need to specify the internal technical implementation details of a software component in the Behaviour Specifications. To be very clear, let me emphasise the word “internal”, in contrast with any publicly exposed, outward-facing behaviour or other mandated functionality, such as business rules.

If the implementation of a component needs to be refactored or changed, the words used in the Behaviour Specifications should NOT need to be changed. The words used in a Behaviour Specification should only need to change if there is a change to a “core business requirement”.

Behaviour Specifications can become unnecessarily brittle and potentially short-lived due to tight coupling that is created when any internal technical implementation concerns are specified. In other words, if internal technical implementation details are specified in the Gherkin, then the cost of maintenance and future enhancements will increase unnecessarily, since it could have been avoided in the first place.

Internal Technical Details — Example #1

For example, say that the low-level technical design has identified there is a component that must store and retrieve items in memory. Perhaps the component uses a Dictionary data structure to store the data, or alternatively, perhaps it uses an Ordered List data structure.

In this example, the type of data structure internally used by the component is completely irrelevant, and the fact that a data structure exists at all also is completely irrelevant in the Behaviour Specification. The only concerns that are relevant are the behaviours related to successfully and unsuccessfully adding and retrieving items, and also perhaps some non-functional performance requirements.

Internal Technical Details — Example #2

In this example, say that there is a behaviour specifying that a component should persist its data, but it unfortunately also says the words “in a database”. The word “database” is an internal implementation detail that makes the Behaviour Specification unnecessarily brittle. If in the future, the implementation is changed to store the data in a file in a file system, the wording of this Behaviour Specification also would have to be changed. This is inefficient, costly and can be avoided!

Alternatively, the wording “data store” is an example of a preferred, generic term that cleverly encapsulates any changes or volatility in implementation. When using the words “data store”, if the internal technical design of the component changes from using a database to storing the data in a file, the wording of the Behaviour Specification does not need to be changed. A behaviour that says “data store” instead of “database” is more resilient to future implementation changes.

Internal Technical Details — Example #3

In the example descriptions in the earlier section What Is A Behaviour Specification Description?, hopefully you noted that there is no indication of how the behaviours are technically implemented internally. In fact, just by reading those descriptions, you would not even know that they are specifically describing the behaviour of a User Interface component. That is intentional! Unlike many Gherkin examples that can be found in BDD literature, there is no typical User Interface wording about “screens”, “pages”, “fields”, “buttons”, “clicks”, or “navigation” etcetera, nor is there any need for those types of words.

These same behaviour descriptions could be copied as-is and used to specify a Web Service component as well as a User Interface! This is a prime example of why the software component context is king — the Behaviour Specifications of related functionality that exists in components separated by an architectural tier can be the same, and it is only the technical implementation specific to that tier that is different.

Internal Technical Details — Example #4

The example descriptions in the earlier section What Is A Behaviour Specification Description? also do not specify the particular technical mechanism by which the address validation service is invoked — for instance, as a Web Service call. That would be internal implementation detail!

It is possible that the solution delivery team could decide it is best in the short-term to implement a tactical solution. In this example, that tactical solution is chosen to be a monkey that physically runs down the corridor and hands a human a piece of paper that has an address printed on it which is to be verified. Whatever tactical or strategic approach is taken, the Behaviour Specification wording should not need to be altered!

To recap, it does not matter how the behaviour is implemented internally within a component. The wording of the Behaviour Specification should only need to change if there is a change to the core business requirement. Behaviour Specifications should be long-lasting and resilient by encapsulating internal implementation volatility. For anyone who is interested in the current, actual technical implementation details, that can be understood from and seen in the test run output.


Next » Specify Relevant Business Capabilities