How do I test a Workflow Service?

WCF Workflow Services are web services which are implemented with activities. Testing them can be a challenge because you must host the activities with a web server or custom host and invoke them by sending a message to them. View the complete walk through on wf.CodePlex.com.

The Workflow Service Library

This sample includes a project created using the Workflow / WCF Workflow Service Application project template named MathServiceLibrary

Open IncrementService.xamlx in the Workflow Designer.
In the Assign activity below the ReceiveRequest and set the properties - we have intentionally caused a bug by adding 2 to the data
To result
Value data + 2

Note: The sample includes a similar service called IncrementServiceParameters.xamlx which uses parameters content for both the Receive and SendReply activities to increment two values.

The Test Project

The sample includes a test project MathServiceLibrary.Tests.  We used Use NuGet to Install Microsoft.Activities.UnitTesting to MathServiceLibrary.Tests.

There are two options for testing.

  1. Unit test with WCF
  2. Unit test by Mocking the Recieve/Send reply activities

Unit Testing with WCF

To support unit testing with WCF we added the following static members to support testing with named pipes and the following test method.
C#
Edit|Remove
private static readonly NetNamedPipeBinding Binding = new NetNamedPipeBinding(); 
private static readonly EndpointAddress ServiceAddress = new EndpointAddress("net.pipe://localhost/IncrementService"); 
 
[TestMethod] 
[DeploymentItem(@"MathServiceLibrary\IncrementService.xamlx")] 
public void IncrementServiceShouldIncrementData() 
{ 
    // Arrange 
    const int InitialData = 1; 
    const int ExpectedData = 2; 
 
    WorkflowServiceTestHost host = null; 
    try 
    { 
        using (host = WorkflowServiceTestHost.Open("IncrementService.xamlx", ServiceAddress)) 
        { 
            var proxy = new ServiceClient(Binding, ServiceAddress); 
            intvalue = InitialData; 
            proxy.Increment(ref value); 
            Assert.AreEqual(ExpectedData, value"Increment did not correctly increment the value"); 
        } 
 
        // The host must be closed before asserting tracking 
        // Explicitly call host.Close or exit the using block to do this. 
 
        // Assert that the Assign activity was executed with an argument named "Value" which contains the value 2 
        host.Tracking.Assert.ExistsArgValue("Assign", ActivityInstanceState.Closed, "Value"2); 
    } 
    finally 
    { 
        if (host != null) 
        { 
            host.Tracking.Trace(); 
        } 
    } 
} 
 

Unit Testing with Mocks and XamlInjector

To unit test with Mocks we use the following code.  This test runs much faster and does not require a hosted WCF service.
C#
Edit|Remove
[TestMethod] 
[DeploymentItem(@"MathServiceLibrary\IncrementService.xamlx")] 
public void IncrementServiceShouldIncrementDataUsingMocks() 
{ 
    // Arrange 
    const int InitialData = 1; 
    const int ExpectedData = 2; 
 
    // Inject the mocks into the service 
    var xamlInjector = new XamlInjector("IncrementService.xamlx"); 
    xamlInjector.ReplaceAll(typeof(Receive), typeof(ReceiveStub)); 
    xamlInjector.ReplaceAll(typeof(SendReply), typeof(SendReplyStub)); 
 
    // Setup the messages 
    var stubExtension = new MessagingStubExtension(); 
 
    // Enqueue a message for the receive activity 
    stubExtension.EnqueueReceive(XName.Get("{http://tempuri.org/}IService"), "Increment", InitialData); 
 
    // Setup the host 
    var host = WorkflowInvokerTest.Create(xamlInjector.GetWorkflowService().Body); 
    host.Extensions.Add(stubExtension); 
 
    try 
    { 
        // Act 
        host.TestActivity(); 
 
        // Assert 
        Assert.AreEqual(2, stubExtension.Messages.Count, "There should be two messages one from the Receive and one from the SendReply"); 
        Assert.AreEqual(ExpectedData, stubExtension.Messages[1].Content, "Increment did not correctly increment the value"); 
    } 
    finally 
    { 
        host.Tracking.Trace(); 
    } 
} 

Task 1 - Run the Test and Diagnose Failure

Assert.AreEqual failed. Expected:<2>. Actual:<3>. Increment did not correctly increment the value

Did the service receive the correct initial value?

Tracking record 11 shows the message that was received and the value 1 is in the message body
11: Activity [14.6] "InternalReceiveMessage" is Closed at 01:42:03.5879
{
    Arguments
        CorrelatesWith: 
        Message: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IService/Increment</a:Action>
    <a:MessageID>urn:uuid:bd4d84b7-0677-446e-885c-40e689088e8a</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">net.pipe://localhost/IncrementService</a:To>
  </s:Header>
  <s:Body>
    <int xmlns="http://schemas.microsoft.com/2003/10/Serialization/">1</int>
  </s:Body>
</s:Envelope>
        noPersistHandle: System.Activities.NoPersistHandle
        Parameter0: System.ServiceModel.Activities.CorrelationHandle
}

Did the Assign activity assign the correct value?

Tracking record 18 shows that the assign activity received the value 3 from the expression which is incorrect.
18: Activity [9] "Assign" is Executing at 01:42:03.5879
{
    Arguments
        Value: 3
}

Task 2 - Correct the Workflow Service