onsdag 14. juni 2017

Testing of debatching scenarios in BizTalk with TransMock - part 1


This blog post is a series of 2 posts about testing debatching scenarios in BizTalk server with TransMock. Why exactly 2? Simply because TransMock has 2 different modes for verifying messages which are received as part of a debatching operation - serial and cascading.
  • Serial mode, which is the default one, is where each and every debatched message is validated in the same way by the same validation step. 
  • Cascading mode is when each debatched message is validated through a dedicated validation step.
In this post I will demonstrate how to utilize the serial mode for testing and verifying batching scenarios. But first let's describe the test scenario. The flow is about a sales order that is released from the ERP and sent for verification of warehouse availability by the warehouse management system.
BizTalk receives an XML message from the ERP, which is debatched in the receive pipeline. Each individual message is then processed by a subscribed orchestration. In the orchestration the request message is mapped to a web service request and then the corresponding web service exposed by the WMS system is invoked. The response from the service is then transformed to a final message type which is put onto a queue to the target logistics system.
The input message contains the sales order with separate product lines, which are debatched. Lets assume that for a particular test scenario there are 4 product lines in the test sales order.
The web service is invoked to verify the availability of the product quontity of each product line.

By now it should be obvious that there is a particular challange in testing this flow with TransMock. The web service endpoint mock should be invoked multiple number of times - in our test case that is 4. How shall the test case be defined in this particular scenario? The answer to this is pretty simple as TransMock supports debatching scenarios out of the box since version 1.2. The code snippet below demonstrates how is this achieved:

    
    [TestMethod]
    [DeploymentItem(@"TestData\SO_ERP_4ProdLines.xml")]
    [DeploymentItem(@"TestData\WS_WMS_Response_Prod1.xml")]
    public void TestSerialDebatching_HappyPath()
    {
        var testCase = new TestCase();
        testCase.Name = "TestSerialDebatching_HappyPath";
    
        var sendSalesOrderStep = new MockSendStep()
        {
            Url = SOWarehouseReservationMockAddresses.Rcv_SO_ERP_Q,
            Encoding = "UTF-8",
            RequestPath = "SO_ERP_4ProdLines.xml"
        };

        var receiveWMSReservationStep = new MockRequestResponseStep()
        {
            Url = SOWarehouseReservationMockAddresses.Snd_WMS_WCF_WebAPI,
            Encoding = "UTF-8",
            ResponsePath = "WS_WMS_Response_Prod1.xml",
            RunConcurrently = true,
            DebatchedMessageCount = 4,
            ValidationMode = MultiMessageValidationMode.Serial              
        };          

        var wmsReservationValidationStep = new TransMock
            .Integration.BizUnit.Validation.LambdaValidationStep()
        {
            ValidationCallback = (data) => {
                // Loading the document from the data stream
                var msgDoc = XDocument.Load(data);

                //fetch the order Id from the message
                var orderId = msgDoc.Root.Elements("OrderID").FirstOrDefault();
                 
                Assert.IsNotNull(orderId, "No OrderID element found");
                Assert.AreEqual(orderId.Value, "5500096856", "OrderID value not as expected");

                return true;
            }
        };

        receiveWMSReservationStep.SubSteps.Add(wmsReservationValidationStep);
        testCase.ExecutionSteps.Add(receiveWMSReservationStep);

        var receiveShipClearanceStep = new MockReceiveStep()
        {
            Url = SOWarehouseReservationMockAddresses.Snd_Logistics_Q,
            Encoding = "UTF-8",              
            RunConcurrently = true,
            DebatchedMessageCount = 4,
            ValidationMode = MultiMessageValidationMode.Serial
        };

        var shipClearValidationStep = new TransMock
            .Integration.BizUnit.Validation.LambdaValidationStep()
        {
            ValidationCallback = (data) =>
            {
                // Loading the document from the data stream
                var msgDoc = XDocument.Load(data);

                //fetch the order Id from the message
                var shipmentRef = msgDoc.Root.Elements("ShipRef").FirstOrDefault();

                Assert.IsNotNull(shipmentRef, "No ShipRef element found");
                Assert.AreEqual(shipmentRef.Value, "1223/5500096856", "ShipRef value not as expected");

                return true;
            }
        };
 
        receiveShipClearanceStep.SubSteps.Add(shipClearValidationStep);
        testCase.ExecutionSteps.Add(receiveShipClearanceStep);

        var testRunner = new BizUnit.BizUnit(testCase);

        testRunner.RunTest();
    }

Note the following properties that are set on the inventoryWebServiceMockStep:
- DebatchedMessageCount = 4 - this property is set to the number of messages that we expect to receive from BizTalk in this step.
- ValidationMode = MultiMessageValidationMode.Serial - this property is about setting the validation mode for debatched messages. This is also the default value of this property. Here it is set just for the purpose of demosntration, otherwise it is not required.

The essential part in the serial validation mode for debatched messages is that for each debatched message received in the mock step instance there will be executed each and every validation step defined in the SubSteps collection.

In our case we have only one LambdaValidationStep instance. This validation step verifies that the order number is as expected on each product line. If the method of using the LambdaValidationStep is new to you, please read the dedicated blog post for it here. LambdaValidationStep was introduced in version 1.2 as an attempt to promote a more flexible, simplified and unified validation programming model for validation logic in BizUnit.

The same is the case with the mock receive step for the ship prepare message to the logistics system. There we have also defined that we expect 4 messages and we have defined one step of type LambdaValidationStep. There we check if the ShipRef element is in place and contains the expected value.

Conclusion

With this method it is relatively simple to define test cases that re-use the same definition of a single mock step to receive and validate multiple debatched messages. In fact this technique is applicable not only in classical debatching scenarios as described in the fictive test case above, but in cases where there are for example looping conditions in the tested flow, resulting in multiple invocations of a single mock step instance.

Jump to the Testing of debatching scenarios in BizTalk with TransMock - part 2 blog post to learn more about the Cascading validation mode

1 kommentar: