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
TestBookmark<T> - allows you to create a bookmark with a name provided in an expression
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