Saturday, January 7, 2017

Windows Workflow Unit Testing

I know people have very mixed opinions about Windows Workflow and, to be honest, so do I. Really I am not even sure if it has much of a future given the little attention Microsoft has given it. However, despite all that and rather your like it or not there are times when you may use it and want to unit test it. The question is how? Well there are not a lot of options but there is one, that for me, has proven valuable.

People tend to use Windows Workflow in a few different ways, so first let me explain how I have use it most. I have never really used it where I programmatically created and instantiate of my own workflow. For me it has pretty much all been using the Windows Workflow designer and using IIS as my workflow host. Then inside those XAML workflows I have custom activities I create and need to test. Do to this I have found one tool that does this pretty well and pretty easy.

Microsoft Activities Unit Testing

It is an old framework but it still gets the job done. There is not a real Dependency injection framework for Windows Workflow activities but with this you can basically use the service locator pattern to do unit testing. You could get fancy with and use the metadata model to store a DI container like Castle Windsor but so far, for me, I have found that activities normally have discreet enough unit of works that I don't need anymore. The CodePlex page has a good how to get setup page. It also has a good how to use the framework page.

What is nice about the framework is it comes with an easy way to get your activity hosted as a Windows Workflow activity with standard in and out arguments so it really behaves just like a Workflow Activity. There are a couple key aspects to getting this setup.

Getting the host setup is easy:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[TestMethod]
public void SumAddsTwoNumbers()
{
    // Arrange
    var activity = new Sum();
    var host = WorkflowInvokerTest.Create(activity);

    // InArguments is a dynamic object of type Microsoft.Activities.WorkflowArguments
    host.InArguments.Num1 = 1;
    host.InArguments.Num2 = 2;

    try
    {
        // Act
        host.TestActivity();

        // Assert
        // Note: The host automatically captures the out arguments
        host.AssertOutArgument.AreEqual("Result", 3);
    }
    finally
    {
        // Note: The host automatically captures tracking
        host.Tracking.Trace();
    }
}

Notice in the above code all you are doing is creating an instance of your normal activity, then using the framework to create a host for it. You can then just setup your InArgs (notice these are dynamic so don't expect intellisense here).

Ok that is great, but what about my dependencies? Here is the magic for that.

In your activity you need to setup a new method override.
1
2
3
4
5
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
    base.CacheMetadata(metadata);
    metadata.AddDefaultExtensionProvider<YourInterface>(() => Your code to create the instance);
}

Adding the default extension provider allows your code later (by the below line of code) to get the instance it should use. If nothing else is provided it, well, uses the default that is setup.

1
this.unitOfWork = context.GetExtension<IUnitOfWork>();

For my code I am assigning whatever extension is setup to my unitOfWork field. This lets me use that object through the rest of my code.

Now you need to get it setup for your unit test.
1
2
3
this.unitOfWork = Mock.Of<IUnitOfWork>(m => m.MytRepository == Mock.Of<IMyRepository>());

this.workflowHost.Extensions.Add(() => this.unitOfWork);

All I need to do is create my mocked object (I used Moq). Then register that mock as a workflowhost extension. Now when I run this code as part of my unit test it will see my registered moq as the extension for that interface and use it instead of the default. But when the code runs in production it will not see any registered extension so it will use the registered default. That is it. I am sure there is even more you can do with this but that should get you up and running with Windows Workflow Activity unit testing.

Here is a look at how to do your assert on what is returned.
1
2
3
var outarg = this.workflowHost.OutArguments["Result"] as List<OrderFulfillment>;
outarg.Count.Should().Be(11);
outarg.Select(p => p).Count(p => p.CustomAttributes == null).Should().Be(0);


Here is another example of how someone used it.
Here is a Stackoverflow thread with some resources as well.

No comments: