Since a WWF activity is just a .Net class, testing is easy unsing our unit test framework. Since workflows and activities are just .Net classes, you construct them as you would any .Net class. The workflow runtime constructs them before it begins an activity's execution. Unit testing the custom workfow activity construction is straight forward as can be seen in the code fragment below.
[TestMethod]
[Description("Blog sample code for unit testing a custom activity initialization.")]
public void Activity1_Initialize()
{
// Unit test the initialization of the activity
Activity1 activity = new Activity1();
PrivateObject po = new PrivateObject(activity);
CodeActivity leftBranch = (CodeActivity) po.GetField("LeftBranch");
CodeActivity rightBranch = (CodeActivity) po.GetField("RightBranch");
CodeActivity intializeData = (CodeActivity)po.GetField("IntializeData");
IfElseBranchActivity ifElseBigOrder = (IfElseBranchActivity) po.GetField("BigOrder");
IfElseBranchActivity noPizzaOrder = (IfElseBranchActivity) po.GetField("NoPizza");
CodeActivity handleException = (CodeActivity) po.GetField("HandleException");
Assert.IsNotNull(leftBranch);
Assert.IsNotNull(rightBranch);
Assert.IsNotNull(intializeData);
Assert.IsNotNull(ifElseBigOrder);
Assert.IsNotNull(noPizzaOrder);
Assert.IsNotNull(handleException);
}
Likewise, I could test an internal custom code activity more directly by invoking the event handler created by the workflow designer when I was creating my custom activity. This also demostrates a best practice of unit testing funtionality with the least dependencies early before assembling this functionality into more complex features. When I create the InitializeData step, I defined the step’s ExecuteCode property to “OnInitialize” in the WWF designer and property page. The WWF designer made an event handler for me in the code file for the Activity1. The first step in my custom activity is the InitializeData step. That unit test might look as follows:
[TestMethod]
[Description("Blog sample code for unit testing a code activity.")]
public void Activity1_InitializeMethod()
{
// Unit test the initialization of the activity
Activity1 activity = new Activity1();
PrivateObject po = new PrivateObject(activity);
po.Invoke("OnInitialize", this, EventArgs.Empty);
Assert.AreEqual(5, activity.PizzaCount, "Expected the number of pizzas to be 5.");
Assert.IsFalse(activity.ErrorsDetected, "Should have no Errors at this time.");
Assert.IsTrue(activity.IsInitialized, "The internal data was initialized.");
}
In the two unit tests above, I have tested a custom activity without the need to deal with the workflow runtime. You could take this approach even further by putting your business logic in an assembly that was external to the custom workflow activity, and having the custom activity call into this business logic when needed. In either case, the unit testing apis are just making calls to .Net classes.
You could also unit test a workflow using our unit testing apis. In the example below, I show how to create a workflow runtime needed to start a workflow. This code tests whether the workflow completed as expected.
[TestMethod]
public void WorkflowUnitTest_StartWorkflowInstance()
{
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
workflowRuntime.WorkflowCompleted += new
EventHandler<WorkflowCompletedEventArgs>(
workflowRuntime_WorkflowCompleted);
workflowRuntime.WorkflowAborted += new
EventHandler<WorkflowEventArgs>(
workflowRuntime_WorkflowAborted);
workflowRuntime.WorkflowTerminated += new
EventHandler<WorkflowTerminatedEventArgs>(
workflowRuntime_WorkflowTerminated);
WorkflowInstance_WaitHandle = new AutoResetEvent(false);
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(
typeof(WorkflowConsoleApplication1.Workflow1));
instance.Start();
WorkflowInstance_WaitHandle.WaitOne();
Assert.AreEqual(1,
Workflow_UnitTest.UnitTestEventCounts[WorkflowEventCategory.Completed]);
}
}
In the sample above, the unit test constructs a default implementation for the workflow runtime (its built into .Net). Having this, we can create an instance of our workflow and begin its execution. The unit test then waits for the workflow to complete and then performs its tests. Although the sample above is simple, it shows what is possible by using existing WWF events within the workflow runtime, WWF objects, and our unit testing framework.
I encourage you to post ideas and suggestions for other articles you may find interesting as it relates to testing workflows. This post just scratches the surface but it is after all a Hello Workflow kind of sample.