Search Wiki:

WF4 Workflow Test Helper (Project Retired)

Utility for helping you to unit test Windows Workflow Foundation (System.Activities from .NET 4)

Download

Microsoft.Activities.UnitTesting

Use Microsoft.Activities.UnitTesting instead

We are happy to announce that this project has been promoted from a sample to a library on our http://wf.codeplex.com site.
The namespace has been changed to Microsoft.Activities.UnitTesting and there have been some other changes but you should be able to migrate your tests by simply using the new library.

  • Test Activities - DiagnosticTrace, TestBookmark
  • Test all kinds of workflows using WorkflowInvoker, WorkflowApplication or WorkflowServiceHost
  • Automatically captures tracking information allowing you to use LINQ to search for tracking data
  • Includes memory based persistence store allowing you to test persistence and correlation keys
  • Captures text from WriteLine activities automatically
  • Use XamlInjection to substitute Mock Activities when testing

How To


Extensions


Test Activities

DiagnosticTrace - allows you to output a System.Diagnostics.Trace message as well as a custom tracking record

DiagnosticTrace.jpg

TestBookmark<T> - allows you to create a bookmark with a name provided in an expression

TestBookmark.png

Utility Classes


WorkflowInvokerTest - Helps you to write unit tests with simple activities while capturing tracking information and output arguments
[TestMethod]
public void ShouldAddGoodSumAssertOutArgument()
{
    var sut = new WorkflowInvokerTest(new GoodSum {x = 1, y = 2});
    sut.TestActivity();
 
    sut.AssertOutArgument.AreEqual("sum", 3);
}

WorkflowApplicationTest - Helps you to write unit tests using WorkflowApplication for workflows that are more complex requiring support for bookmarks. Also captures all event args and tracking information

[TestMethod]
public void ShouldTestActivityWithBookmarks()
{
    // Arrange
    var wfAppTest = WorkflowApplicationTest.Create(new ActivityWithBookmarks());
 
    // Act
    wfAppTest.TestActivity();
 
    // Assert
    Assert.IsTrue(wfAppTest.WaitForIdleEvent());
 
    Assert.IsTrue(wfAppTest.Bookmarks.Contains("GetNumber2"));
    Assert.AreEqual(BookmarkResumptionResult.Success,
                    wfAppTest.TestWorkflowApplication.ResumeBookmark("GetNumber2", 2));
 
    Assert.IsTrue(wfAppTest.WaitForIdleEvent());
    Assert.IsTrue(wfAppTest.Bookmarks.Contains("GetNumber3"));
    Assert.AreEqual(BookmarkResumptionResult.Success,
                    wfAppTest.TestWorkflowApplication.ResumeBookmark("GetNumber3", 3));
 
    Assert.IsTrue(wfAppTest.WaitForCompletedEvent());
    wfAppTest.AssertOutArgument.AreEqual("Number", 6);
}

AssertOutArgument - Allows you to assert that an out argument with the given name exists, is of the same type as the expected value and provides AreEqual, AreNotEqual, IsNull, IsNotNull etc. Exposed through WorkflowTestApplication class as a property
    // Assert
    wfAppTest.AssertOutArgument.AreEqual("Greeting", expectedGreeting);
    wfAppTest.AssertOutArgument.AreNotEqual("WorkflowThread", testThread);
    wfAppTest.AssertOutArgument.AreEqual("WorkflowThread", actionThread);

WorkflowTestResults - Captures all the event arguments provided from WorkflowApplication along with tracking records - access through the WorkflowTestApplication.Results property

AssertTracking - Asserts that tracking records exist after test run

WorkflowServiceTestHost - Hosts a WorkflowService (.xamlx) and captures tracking for testing
[TestMethod]
[DeploymentItem(@"WorkflowTestHelper.Tests.Activities\TestService.xamlx")]
public void ShouldHostService()
{
    var trackingProfile =
        new TrackingProfile
            {
                Queries =
                    {
                        new ActivityStateQuery
                            {
                                ActivityName = "ReceiveRequest",
                                States = {"Executing"},
                            },
                        new ActivityStateQuery
                            {
                                ActivityName = "SendResponse",
                                States = {"Executing"},
                            },
                    }
            };
 
 
    using (var host = WorkflowServiceTestHost.Open("TestService.xamlx", _serviceAddress.Uri, trackingProfile))
    {
        var client = ChannelFactory<ITestService>.CreateChannel(_binding, _serviceAddress);
        var response = client.GetData(1);
        Assert.AreEqual("1", response);
 
        host.Tracking.Trace();
 
        // Find the tracking records for the ReceiveRequest and SendResponse
 
        // Activity <ReceiveRequest> state is Executing
        AssertTracking.ExistsAt(host.Tracking.Records, 0, "ReceiveRequest", ActivityInstanceState.Executing);
 
        // Activity <SendResponse> state is Executing
        AssertTracking.ExistsAt(host.Tracking.Records, 1, "SendResponse", ActivityInstanceState.Executing);
    }
}

MemoryStore - A memory based PersistenceStore allows you to test workflows that become idle, persist and reload. WorkflowServiceTestHost automatically adds this as the persistence store.

/// <summary>
///   Demonstrates how to test a service with correlation
/// </summary>
/// <remarks>
///   Be sure to enable deployment - the xamlx file must be deployed
/// </remarks>
[TestMethod]
[DeploymentItem(@"WorkflowTestHelper.Tests.Activities\ServiceWithCorrelation.xamlx")]
public void ShouldCorrelateServiceCalls()
{
    using (var testHost = new WorkflowServiceTestHost("ServiceWithCorrelation.xamlx", _serviceAddress.Uri))
    {
        // Add an idle behavior to unload as soon as idle is detected
        testHost.Host.Description.Behaviors.Add(new WorkflowIdleBehavior
                                                    {TimeToUnload = TimeSpan.Zero});
        testHost.Open();
 
        var client = ChannelFactory<IServiceWithCorrelation>.CreateChannel(_binding, _serviceAddress);
        Trace.WriteLine("Test Client: Sending GetData(1)");
        var response = client.GetData(1);
        Assert.AreEqual("1", response.Text);
 
        Trace.WriteLine(string.Format("Test Client: Received GetData response {0} with key {1}", response.Text,
                                        response.Key));
 
        // Wait for unload
        Thread.Sleep(1000);
 
        // If you want to see what is in the memory store, dump it
        // MemoryStore.Dump();
 
        Trace.WriteLine(string.Format("Test Client:  Sending GetMoreData(2, {0})", response.Key));
        var secondResponse = client.GetMoreData(2, response.Key);
        Assert.AreEqual("2", secondResponse.Text);
 
        Trace.WriteLine(string.Format("Test Client: Received GetMoreData response {0} with key {1}", secondResponse.Text,
                                        secondResponse.Key));
 
        testHost.Tracking.Trace();
 
        MemoryStore.DisplayCommandCounts();
        Assert.AreEqual(1, MemoryStore.LoadWorkflowCommandCount);
        Assert.AreEqual(1, MemoryStore.LoadWorkflowByInstanceKeyCommandCount);
        Assert.AreEqual(3, MemoryStore.SaveWorkflowCommandCount);
        Assert.AreEqual(1, MemoryStore.CreateWorkflowOwnerCommandCount);
    }
}

XamlInjector - Allows you to substitute mock activities when testing Activities or WorkflowServices
[TestMethod]
[DeploymentItem(@"WorkflowTestHelper.Tests\TestInject.xaml")]
public void ShouldReplaceTypesInXaml()
{
    var xamlInjector = new XamlInjector("TestInject.xaml");
 
    // The first TestActivity1 will not be replaced - will add 1 to sum
 
    // Replace the second TestActivity1 with TestActivity2 - will add 2 to sum
    xamlInjector.ReplaceAt(1, typeof (TestActivity1), typeof (TestActivity2));
 
    // Replace third TestActivity1 with TestActivity3 - will add 3 to sum
    xamlInjector.ReplaceAt(2, typeof (TestActivity1), typeof (TestActivity3));
 
    // Replace all (2) TestActivity4 with TestActivity5 - will add 10 to sum
    xamlInjector.ReplaceAll(typeof (TestActivity4), typeof (TestActivity5));
    var activity = xamlInjector.GetActivity();
 
    Debug.WriteLine(string.Format("Invoking Injected XAML activity {0}", activity.GetType()));
 
    var wiTest = new WorkflowInvokerTest(activity);
 
    // Act
    wiTest.TestActivity();
 
    // Total should be 1+2+3+10=16
    wiTest.AssertOutArgument.AreEqual("sum", 16);
}


History


v 1.5


v 1.4

  • Changes to support XamlInjection
  • Added WorkflowInvokerTest

v 1.3

  • Modified TestBookmark to use a generic argument TestBookmark<T> and changed the namespace to WorkflowTestHelper.Activities. If you used this activity in XAML just check the namespace declaration and add the type argument to your XAML <wa:TestBookmark x:TypeArguments="x:String"
  • Modified WorkflowApplicationTest<T> to create the WorkflowApplication when constructed and accept an input dictionary in the Create call. This allows you to access the WorkflowApplication member sooner and add extensions etc.
  • Added WorkflowApplicationTest.TextLines property - WorkflowApplicationTest will add a StringWriter extension so it will automatically capture all WriteLine activity output.
  • Added WorkflowApplicationTest.Bookmarks property that contains a list of strings with bookmark names.

v 1.2

  • Added support for MemoryStore
Last edited Jul 25 2011 at 3:07 PM  by RonJacobs, version 26
Comments
fleed wrote  Nov 1 2010 at 6:43 AM  
It saves lot of time debugging and verifying workflows.
Thank you very much!

Updating...
Page view tracker