Recently I read a posting on our forum asking how would someone unit test workflows from the Windows Workflow Foundaiton (WWF).  This article, and I hope ones that will follow, will show how easy this really is to unit test portions of your workflows.  This article begins my journey through the landscape of unit testing workflows and their activities using our unit test framework

I start my sample with three Visual Studio projects.  The first is a workflow activity library, the second is a sequential workflow console application, and the third is a unit test project. The focus of this article is to get us started, so our samples are fairly simple.  My custom activity is looks as follows in the workflow designer.

 Custom Activity

The custom activity contains a code activity that initializes data for the if/else decision step.  The decision has a left branch and a right branch.  Each branch executes a code activity.  The logic of the activity is such that if the pizza order size is zero it takes the left branch, otherwise it takes the right branch. 

 The workflow is a sequential workflow that just executes our custom activity.  It looks as follows in the workflow designer.

Simple Workflow 

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.