BizTalk 2004 -> Testing Orchestrations using Nunit, Submit BizTalk Adapter and a Normalizer Pattern

To create a Nunit test of a single orchestration or a series of orchestrations is not a natural process.
I came up with this method for testing Orchestrations using Nunit when I was working on a BizTalk project. The client required that Nunit tests be performed on the orchestrations that were developed. It is also part of the first demo of a WebCast I did on March 28 2005 called: BizTalk Server 2004 Implementing Enterprise Integration Patterns. It can be viewed HERE as a recorded Webcast.
In the below example a Normalizer pattern Orchestration is being tested. The full BizTalk solution can be downloaded at the end of this blog.
The below details are somewhat involved, but if you download the above solution and install, analyze and run the BizTalk demos, it will become clear.

Getting back to the project. I was applying Patterns to this particular integration solution. This in turned produced a more modular type of solution with a Normalizer (this is a pattern) Orchestration and a Message Aggregator (this is another pattern) Orchestration. Because the solution was more modular in nature, it also made it easier to test. Additionally this method uses a Test harness Orchestration to asynchronously call the Normalizer Orchestration. In the production environment the test harness Orchestration would be replaced by another orchestration.

This testing method will not work for every type of Orchestration scenario, but works best if the Orchestration is modular in nature (i.e. implements some pattern). Even if you do not use this method for testing Orchestrations, this example shows:
1) A Nomalizer Pattern (two different BizTalk implementations are discussed).
2) How to pass multi-part messages between orchestrations.
3) How to conditionally turn an asynchronous call to another orchestration into a Synchronous Call.

The basic flow to test the Normalizer Orchestration is to:
1) Use .NET code in a Nunit Test Harness to produce a XML message that will ultimately be passed to the Normalizer Orchestration.
2) The .NET Code then calls the BizTalkMessaging.SubmitSyncMessage method to submit the XML message
to a Request-Response Receive Physical Port that uses the Submit Adapter. Because the SubmitSyncMessage method is used, the code will then wait for a response back from the Receive Port.
3) The Receive Port then publishes the incoming message from the Nunit test harness into BizTalk.
4) A Test harness Orchestration then subscribes to the message published by the Submit Adapter. This Orchestration will then use the Start Shape and asynchronously call the Normalizer Orchestration passing a couple of parameters, one containing the XML message for the Normalizer to work on and the other
being a Self Correlated Port.
5) The Normalizer Orchestration will then process the message and then construct a multipart message to be sent back to the Test Harness Orchestration. The Normalizer Orchestration will then call back to the Test Harness Orchestration using the Self Correlated Port passed in as a parameter.
6) The Test Harness Orchestration then constructs a response message using the results from the Normalizer Orchestration. This message is then passed back to the Nunit test harness code, using the the Logical Request-Response logical port in the Test Harness Orchestration.
7) The Request-Response Receive Physical Port described in 2) will then send the response message back to the .NET Nunit Test Harness code via the BizTalkMessaging.SubmitSyncMessage call.
8) The .NET code in the Nunit Test Harness can then interrogate the message sent back, and perform some tests on this message.

Therefore the important pieces to this are:
1) .Net code in the Nunit Test Harness
2) The Submit Direct Adapter
3) A Test Harness Orchestration. In a production environment, this orchestration would be replaced by another orchestration that will call the Actual Orchestration to be tested as in 4).
4) The Actual Orchestration being tested, in this case the Normalizer Orchestration.

To go into more detail:

1) In a Nunit test harness, submit a message
to BizTalk. (Note: this is not the full code)

string submitURI = "submit://MessageNormalizer";
btm = new BizTalkMessaging();
System.Xml.XmlDocument xmlDomRollsFromMillOne = New System.Xml.XmlDocument();
xmlDomRollsFromMillOne.LoadXML(@"c:\somexml.xml")
IBaseMessage responseMsg = null;
responseMsg = btm.SubmitSyncMessage(btm.CreateMessageFromString(submitURI,xmlDomRollsFromMillOne.OuterXml));
// Note: Because using the SubmitSyncMessage method, this code will now wait for the response message coming back.

Note:
Nunit 2.2.0, can be downloaded HERE

Note:
SubmitDirect BizTalk adapter can be installed by running ->
C:\Program Files\Microsoft BizTalk Server 2004\SDK\Samples\Adapters\SubmitDirect\Setup.bat
Also read about the SubmitDirect Adapter sample HERE

For users of BizTalk 2000 and BizTalk 2002, the SubmitDirect adapter replaces the BizTalk 2000 and 2002 API calls Submit and SubmitSync. The SubmitDirect Adapter allows the submittal of messages to BizTalk Server programmatically. Therefore you can write some C# or VB.Net code that can submit a message to BizTalk server and optionally receive back a response.

2) Create a Request-Response Receive Physical Port that uses the Submit Adapter as below:

3) Create some common orchestration types to help in the testing:

a) Multi-part Message Type: mpMsgTypeRolls
(This multipart is used to pass information to the Normalizer Orchestration)
mpMsgTypeRolls contains Message Parts:
i) msgCallBackToOrchestrationFlag. This flag is set to "1" if the Normalizer Orchestration is to call back
to the calling Orchestration. Therefore in the development/testing environment this flag is set to "1".
In the production environment this flag is set to "0". This is so I can use the Normalizer Orchestration
in the production environment without modifying anything and just changing the passed flag to "0"
ii) msgXMLRolls. This is a string of XML that contains the Roll Production Orders.

b) Multi-part Message Type: mpMsgTypeTestHarnessResults
(This multipart is used to pass information from the Normalizer Orchestration back to the test harness orchestration.)
mpMsgTypeTestHarnessResults contains Message Parts:
i)   msgErrorMessage. This is a string that will be set with any error message.
ii)  msgNumberOfRollsSplit. This is populated by Message Splitter Orchestration
iii) msgXmlRolls. This is returned with the mapped normalized message.

c) Multi-part Message Type: mpMsgTypeInvalidMessage
(This multipart is used to pass information from the Normalizer Orchestration, to the Invalid Message Orchestration.
mpMsgTypeTestHarnessResults contains Message Parts:
i) msgInvalidXMLMessage. This contains the invalid xml that the Normalizer Orchestration cannot parse.

d) Port Type: PortTypeTestHarnessResults
This port type is used to create a port that will be passed as a parameter into the Normalizer Orchestration.
The Normalizer Orchestration will then call back on this port to the calling orchestration.

4) The test harness orchestration then receives the message from the Nunit test harness via the Submit Receive Port, and then in turn calls the Normalizer Orchestration with the following parameters:
i) mpMsgRollsRequest. This multipart message is populated with the XML message to normalize and a flag to
indicate to call back to this particular orchestration.
ii) PortReceiveBackTestHarnessResults. Port of type PortTypeTestHarnessResults. This is a Direct - Self Correlating Port.


The Normalizer Orchestration can then call back to the test harness orchestration even though the
test harness orchestration called the Normalizer Orchestration asynchronously. A good discussion of Direct
Port Binding Types can be found HERE 

5) The Normalizer Orchestration then receives the information from passed parameters and
then Normalizes the message. Because an untyped message is passed to this orchestration, it must first determine the map to invoke. Therefore a property on the message is interrogated to determine the type of message :
varStrMessageType = msgIncomingRolls(BTS.MessageType);
varStrMessageType is then populated with the correct message type such as -> http://RollsFromMillOne#Rolls.
This is of course the TargetNameSpace#RootNodeName combination of the incoming message. A decision shape in this orchestration will then decide what map will be used to Normalize the message based on the contents of the Message Type. If the message is unrecognized, then the InValid MessageChannel Orchestration is called. (This is another pattern).

6) Once the Normalized message has been processed, the Test Harness Orchestration is called back from the Normalizer Orchestration using the port PortCallBackResults that was passed as a parameter by the calling test harness orchestration. This is how an asynchronous call can be conditionally turned into a synchronous call.

7) The Test Harness Orchestration then calls back to the to the Submit Direct Test harness code in 1).This code, can then perform tests on the results sent back.


For a comprehensive Nunit Test Framework please go HERE


Another Normalizer Pattern
-------------------------------

A much simpler method to create a Normalizer pattern in BizTalk is by specifying a Set of Maps in
a Physical Receive Port or Physical Send Port as below:

Depending on message type of the incoming message, one of the following maps will be invoked
producing the normalized message:

MessageType Map Invoked
http://RollsFromMillOne#Rolls Map_RollsFromMillOne_To_RollsInternal.btm
http://RollsFromMillTwo#Rolls Map_RollsFromMillTwo_To_RollsInternal.btm
http://RollsFromMillThree#Rolls Map_RollsFromMillThree_To_RollsInternal.btm


In the above scenario, no orchestrations are required and all the necessary mapping can be
accomplished in Physical Receive or Send Ports


Again the full sample can be downloaded HERE. To install and run, Read the ReadMe.txt file in the zip file, before unzipping and installing. Also included with the sample are a Splitter Pattern Orchestration and an Aggregator Pattern Orchestration that will be discussed in future blog entries.