fredag 11. mars 2016

Testing a real world BizTalk integration with TransMock - Part 3

In the previous post of the series "Testing a real world BizTalk integration with TransMock" I described how to utilize the TransMock framework for testing the goods reservation sub-process of the SO procurement process implementation. 
In this post I will continue with a new test that will exercise the happy path of the subflow for shipment creation . The test case is therefore called CreateShipment_HappyPath.

A quick refresher of the functional flow for this sub-process:
  • Once a shipment is created and confirmed in the ERP a shipment request message is generated and sent to the BizTalk server
  • The integration processes this message and produces as a result an EDI X12 204-Transportation tender request message which is sent to an external shipping partner
  • The shipping partner then response with an EDI X12 990-Tender response message which contains the response to whether the shipment can be picked and when.
  • This response is processed by the integration and as a result of this a shipment confirmation message is sent to the ERP.

The process of creating the test case is again divided in 2 stages - preparation and implementation of the test case.

Preparation
In this stage I will again prepare the files that the test case will be sending to BizTalk. From the flow diagram in the first post we can identify the following messages that are being sent to Biztalk server:
  • Shipment creation request - sent from the ERP to initiate the shipment management process. This is a an XML message representing a customized Shipment entity from Dynamics AX.
  • Shipment confirmation - sent from the external shipment provider as a response to a shipment request. This is a standard EDI X12 990 - Tender response message, which is a flat file.
The messages will reside in the following files:
  • NewShipment_SingleLine_HappyPath.xml - shipment creation request containing a single line item
  • NewTenderResponse_SingleLine_HappyPath.edi - the tender response message.
The first message is based on the Shipment entity XML in Dynamics and contains purely data related to the actual shipment. The below snippet shows how it looks like:
... 
ShipmentCreateRequest_HappyPath.xml
...
<?xml version="1.0" encoding="utf-8" ?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
  <Header>
    <MessageId>{2002A291-8AA4-4405-BC10-383C30376F76}</MessageId>
    <Action>http://schemas.microsoft.com/dynamics/2008/01/services/ShipmentService/insert</Action>
    <RequestMessageId>{13A5A1E4-D248-4361-B484-791AE121C952}</RequestMessageId>
  </Header>
  <Body>
    <MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
      <Shipment xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment">
        <DocPurpose>Original</DocPurpose>
        <SenderId>ERP</SenderId>
        <ShipmentTable class="entity">
          <_DocumentHash>de321661b44419fc5052246e6274f726</_DocumentHash>
          <Deadline>2012-12-31</Deadline>
          <DeliveryAddress>Industriveien 133 N-0944 Oslo Norway</DeliveryAddress>
          <DeliveryCity>Oslo</DeliveryCity>
          <DeliveryCountryRegionId>NO</DeliveryCountryRegionId>
          <DeliveryDate>2015-12-31</DeliveryDate>
          <DeliveryDateControlType>None</DeliveryDateControlType>
          <DeliveryName>Office Supplies Inc.</DeliveryName>
          <DeliveryStreet>Industriveien  133</DeliveryStreet>
          <DeliveryZipCode>N-0944</DeliveryZipCode>
          <DlvMode>UPS</DlvMode>
          <RecId>5637144586</RecId>
          <RecVersion>1</RecVersion>
          <Reservation>None</Reservation>
          <ReturnReplacementCreated>No</ReturnReplacementCreated>
          <ReturnStatus>None</ReturnStatus>
          <ShipCarrierBlindShipment>No</ShipCarrierBlindShipment>
          <ShipCarrierDlvType>Misc</ShipCarrierDlvType>
          <ShippingDateRequested>2015-03-01</ShippingDateRequested>
          <SalesOrderTable class="entity">
            <SalesGroup>CSG-OTH</SalesGroup>
            <SalesId>00016_036</SalesId>            
            <SalesName>Express Delivery Parts - KNERTEN AS</SalesName>
            <SalesOrderNumber>SO876-987-1</SalesOrderNumber>
            <CompanyLegalName>KNERTEN AS</CompanyLegalName>
            <VendorIdentificationNumber>912555642</VendorIdentificationNumber>
            <ReferenceNumber>SO876-987</ReferenceNumber>
            <ContactName>Tester Testersen</ContactName>
            <ContactNumber>47124545</ContactNumber>
          </SalesOrderTable>
          <LoadTable class="entity">
            <LoadId>LD876-987-001</LoadId>
            <Status>Requested</Status>
            <TotalWeight>109.4</TotalWeight>
            <WeightUnit>KG</WeightUnit>
            <TotalVolume>10.4</TotalVolume>
            <VolumeUnit>CM3</VolumeUnit>
            <LoadLinesTable class="entity">
              <ContainerType>PLT</ContainerType>
              <ContainerHeight>10.4</ContainerHeight>
              <ContainerWidth>10.4</ContainerWidth>
              <ContainerBredth>10.4</ContainerBredth>
              <ContainerWeight>10.4</ContainerWeight>
              <NumOfCases>12.0</NumOfCases>
              <Quantity>1.0</Quantity>
            </LoadLinesTable>
          </LoadTable>
        </ShipmentTable>
      </Shipment>
    </MessageParts>
  </Body>
</Envelope>

The second message contains the response to the tender request which was sent as a result of processing the new shipment message as received by the ERP. It contains a confirmation for acceptance of the tender request and looks like this:
 
ISA*00*          *00*          *07*96548889       *07*92548682       *111219*1747*U*00603*000002104*0*P*:~
GS*SM*4405197800*999999999*20111219*1747*2100     *X*004010~
ST*990*210400003~
B1*SCAC*0987-789-1*20161218*A~
L11*0987-789-1*CR*LD00123009~
SE*4*210400003~
GE*1*2104~
IEA*1*000002104~

It should be noted that this file will work only for a specific Partner agreement as certain header settings as well as the record delimiters are specific to given agreement.
 
I add then those files to the TestData folder and set their properties so that they will always be copied to the test deployment folder.
 
Finally I define the test method and decorate it with the DeploymentItem attributes accordingly:
...
[DeploymentItem(@"TestData\NewShipment_SingleLine_HappyPath.xml")]
[DeploymentItem(@"TestData\NewTenderResponse_SingleLine_HappyPath.edi")]
public void CreateShipment_HappyPath()
{
    TestCase testCase = new TestCase(){
        Name = "CreateShipment_HappyPath";
    }
}
...
Test implementation
Now it is time to implement the test case. For the shipment request message that comes from the ERP we will utilize the MockSendStep as we will be mocking the sending of that message. For receiving the EDI tender request which is produced by the integration as result of processing the shipment request we shall utilize the MockReceiveStep accordingly.

The code snippet below demonstrates how the shipment request is sent to BizTalk:

...
MockSendStep erpShipmentRequestStep = new MockSendStep(){
    URL = SOProcurementMockAddresses.ERP_ShipmentCreateRequestIn_MSMQ,
    RequestPath = "ShipmentCreateRequest_HappyPath.xml",
    Encoding = "UTF-8",
    Timeout = 30
};
testCase.ExecutionSteps.Add(erpShipmentRequestStep)
...

The next code snippet shows how the reception of the EDI tender request is implemented:

...
var extTransportTenderReceiveRequestStep = new MockReceiveStep ()
{
    Url = SalesOrderProcurementMockAddresses.SHP_ShipmentRequestEDI_SFTP,                
    Encoding = "UTF-8",
    Timeout = 10
};
testCase.ExecutionSteps.Add(extTransportTenderReceiveRequestStep);
...

There are no particular validations performed on this message due to its format. However it will be good idea to be able to validate the header contents as well as the different important expected fields and corresponding values in the payload. As for certain well-known values from EDI X12 standard, these are validated in the send pipeline thus receiving a message in the mock step is an indication that all such validations passed with success in the pipeline.

The next code snippet shows the implementation of sending the shipment response message:
...
var extTenderResponseSendStep = new MockSendStep()
{
    Url = SalesOrderProcurementMockAddresses.SHP_ShipmentTenderResponse_SFTP,
    RequestPath = "NewTenderResponse_SingleLine_HappyPath.edi",
    Encoding = "UTF-8",
    Timeout = 10
};
testCase.ExecutionSteps.Add(extTenderResponseSendStep);
...

Finally we receive the shipment confirmation response in the ERP format as demonstrated below:
...
var extShipmentConfirmationSchema = new SchemaDefinition(){
    XmlSchemaNameSpace = "http://schemas.microsoft.com/dynamics/2011/01/documents/Message",
    XmlSchemaPath = @"..\..\..\Schemas\ERP\MessageEnvelope.xsd"               
};
var extShipmentConfirmationValidationStep = new XmlValidationStep();
extShipmentConfirmationValidationStep.XmlSchemas.Add(extShipmentConfirmationSchema);
var xpathShipmentId = new XPathDefinition()
{
    XPath = "/*[local-name()='ShipmentConfirmation' and namespace-uri()='http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment']/*[local-name()='ShipmentTable' and namespace-uri()='http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment']/*[local-name()='ShipmentId' and namespace-uri()='http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment']/text()",
    Value = "0987-789-1"
};
extShipmentConfirmationValidationStep.XPathValidations.Add(xpathShipmentId);            
var xpathShipmentConfirmationStatus = new XPathDefinition()
{
    XPath = "/*[local-name()='ShipmentConfirmation' and namespace-uri()='http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment']/*[local-name()='ShipmentTable' and namespace-uri()='http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment']/*[local-name()='ConfirmationStatus' and namespace-uri()='http://schemas.microsoft.com/dynamics/2008/01/documents/Shipment']/text()",
    Value = "Accept"
};
extShipmentConfirmationValidationStep.XPathValidations.Add(xpathShipmentConfirmationStatus);
var extShipmentConfirmationReceiveStep = new MockReceiveStep()
{
    Url = SalesOrderProcurementMockAddresses.ERP_ShipmentConfirmOut_MSMQ,
    Encoding = "UTF-8",
    Timeout = 10
};
extShipmentConfirmationReceiveStep.SubSteps.Add(extShipmentConfirmationValidationStep);
            
testCase.ExecutionSteps.Add(extShipmentConfirmationReceiveStep);
...

Here we perform message contents validation by evaluating several important fields which will indicate the correctness of the received response. These fields are the Shipment Id and the response confirmation state from the shipping provider.

That is all folks! We just need to add the code for executing the test and simply run it:

...
var bizUnit = new BizUnit();
bizUnit.Run(testCase);
...

With the 2 test cases presented so far I have been able to functionally test simply happy path scenarios for a relatively complex integration solution long before it will be deployed to any system or acceptance test environment.
Easily and elegantly as testing of BizTalk integrations should always have been!

Have Fun!

This post is currently the last one in the series. The sample solution which contains the tests described in this blog post series can be downloaded from here. Stay tuned!