Ron Jacobs

Windows Workflow Foundation

How to Unit Test WF4 Workflows

How to Unit Test WF4 Workflows

Rate This
  • Comments 5

Recently I asked you to vote on topics you want to see on endpoint.tv. Coming in at #3 was this question on how to Unit Test WF4 Workflows.

Because Workflows range from very simple activities to long and complex multi-step processes this can be a very challenging.  I found after writing many activities that I began to see opportunities to create a library of test helpers which became the WF4 Workflow Test Helper library.

In this post I’ll show you how you can test a simple workflow using one of the helpers from the library.

Watch endpoint.tv - How To Unit Test Workflows
Download Workflow Test Helper (including this sample code) from MSDN Code Gallery

Testing Activity Inputs / Outputs

If you have a simple activity that returns out arguments and does not do any long-running work (no messaging activities or bookmarks) then the easiest way to test it is with WorkflowInvoker.

The Workflow we are testing in this case contains an activity that adds two Int32 in arguments and returns an out argument named sum

image

The easiest way to test such an activity is by using Workflow Invoker

[TestMethod]

public void ShouldAddGoodSum()
{
var output = WorkflowInvoker.Invoke(new GoodSum() {x=1, y=2});

Assert.AreEqual(3, output["sum"]);
}

While this test is ok it assumes that the out arguments collection contains an argument of type Int32 named “sum”.  There are actually several possibilities here that we might want to test for.

  1. There is no out argument named “sum” (names are case sensitive)
  2. There is an argument named “sum” but it is not of type Int32
  3. There is an argument named “sum” and it is an Int32 but it does not contain the value we expect

Any time you are testing out arguments for values you have these possibilities.  Our test code will detect all three of these cases but to make it very clear exactly what went wrong I create a test helper called AssertOutArgument.

Now let’s consider what happens if you simply changed the name of the out argument from “sum” to “Sum”.  From the activity point of view “Sum” is the same as “sum” because VB expressions are not case sensitive.

But our test code lives in the world of C# where “Sum” != “sum”.  When I make this change the version of the test that uses Assert.AreEqual gets this error message

Test method TestProject.TestSumActivity.ShouldAddBadSumArgName threw exception:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.

If you have some amount of experience with Workflow and arguments you know that the out arguments are a dictionary of name/value pairs.  This exception simply means it couldn’t find the key – but what key?

Using WorklowTestHelper

  1. Download the binaries for WorkflowTestHelper
  2. Add a reference to the WorkflowTestHelper assemblies
  3. Add a using WorkflowTestHelper statement

To use AssertOutArgument we change the test code to look like this

[TestMethod]

public void ShouldAddGoodSumAssertOutArgument()
{
var output = WorkflowInvoker.Invoke(new GoodSum() { x = 1, y = 2 });

AssertOutArgument.AreEqual(output, "sum", 3);
}

Now the error message we get is this

AssertOutArgument.AreEqual failed. Output does not contain an argument named <sum>.

Wrong Type Errors

What if you changed the workflow so that “sum” exists but it is the wrong type?  To test this I made “sum” of type string and changed the assignment activity expression to convert the result to a string.

The Assert.AreEqual test gets the following result

Assert.AreEqual failed. Expected:<3 (System.Int32)>. Actual:<3 (System.String)>.

This is ok but you do look twice – Expected:<3 then Actual:<3 but then you notice the types are different.

AssertOutArgument is more explicit

AssertOutArgument.AreEqual failed. Wrong type for OutArgument <sum>. Expected Type: <System.Int32>. Actual Type: <System.String>.

Summing It Up

AssertOutArgument covers three things, make sure the arg exists, make sure it is the correct type and make sure it has the correct value.  Of course there is much more to the WF4 Workflow Test Helper that I will cover in future posts.

  • As the person that published that question, it's really cool to see you respond to it so quickly. Thanks for the article and references. I've started toying w/ the test helper and it's already proven useful.

  • Whats with the double-click on the code boxes popping up a tiny yellow window I cant close (Firefox)?

  • @stevescottwork - sorry about that - it is an artifact of the code syntax highlighter tool on MSDN blogs.  In IE it closes when I click inside the yellow box again.

  • Lets say some of my InArgs are custom objects...how do I create the Workflow Object.  I can't use .Set becasue I dont have the context object

    IObjectScope scope = RTODataLayer.Private.ObjectScopeProvider1.GetNewObjectScope();

    RTODataLayer.Request request = Helpers.Helper.CreateSampleRequest(scope, 1, false);

    Assert.IsNotNull(request);

    CreateApproval flow = new CreateApproval() { Scope = scope, RequestID = request.RequestID, ApprovalChainLevel = 1 };

    So it fails on the last line with a Failed to convert IObjectScope to InArgument

    Good series so far :)

  • @stevescottwork you can always use an IDictionary<string,object> to create the input arguments - that will work.

Page 1 of 1 (5 items)