The Problem Solver

Tell me and I will forget
Show me and I will remember
Involve me and I will understand
- Confucius -

Unit testing custom workflow activities

Most people consider unit testing of custom workflow activities to pretty much impossible. Sure you can create a dummy test workflow containing your new activity, new up a WorkflowRuntime, create a WorkflowInstance and start it. But just think about all the dependencies here with the extra dummy workflow and the complete WorkflowRuntime with all its dependencies. Hardly a unit test for an activity but more like an integration test. Now there is nothing wrong with integration tests, they are very useful and necessary, but they do not give the speedy and dynamic test coverage you expect and need from a unit test.

So is this really the case?

Lets look at a few simple examples. This first example is really simple, in fact simple enough to say unrealistic but lets take a look anyway:

public partial class WriteLineActivity1 : Activity
{
    public WriteLineActivity1()
    {
        InitializeComponent();
    }

    public string Message { get; set; }

    protected override ActivityExecutionStatus Execute(
        ActivityExecutionContext executionContext)
    {
        if (!string.IsNullOrEmpty(Message))
            Console.WriteLine(Message);

        return ActivityExecutionStatus.Closed;
    }
}

This can easily be tested with a unit test like this.

/// <summary>
///A test for Execute
///</summary>
[TestMethod()]
[DeploymentItem("WFUnitTest1.exe")]
public void ExecuteTest()
{
    WriteLineActivity1_Accessor target = 
        new WriteLineActivity1_Accessor();
    ActivityExecutionContext executionContext = null; 
    ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
    ActivityExecutionStatus actual;
    
    actual = target.Execute(executionContext);

    Assert.AreEqual(expected, actual);
}

So that at least proves some activities can be unit tested Smile

 

But admittedly this is an oversimplification. In a more realistic activity the actual Console.WriteLine(), or whatever the activity needs done, would be implemented in a workflow runtime service so its implementation can be changed independently of the workflow. And that means using the ActivityExecutionContext to retrieve a reference to the service.

Now this is where most people stop because the ActivityExecutionContext is a sealed type and sealed types cannot be mocked! But that isn't quite true because TypeMock can mock just about anything!

So lets take look at slightly more realistic sample.

public partial class WriteLineActivity2: Activity
{
    public WriteLineActivity2()
    {
        InitializeComponent();
    }

    public string Message { get; set; }

    protected override ActivityExecutionStatus Execute(
        ActivityExecutionContext executionContext)
    {
        if (!string.IsNullOrEmpty(Message))
        {
            WriteLineService2 service = 
                executionContext.GetService<WriteLineService2>();

            service.WriteLine(Message);
        }

        return ActivityExecutionStatus.Closed;
    }
}

This activity does the same job as the first one but delegates the actual work to this, simple, runtime service:

public class WriteLineService2
{

    public void WriteLine(string message)
    {
        Console.WriteLine(message);
    }
}

So lets create a few unit tests for this activity. The first test checks the behavior when the message is empty:

/// <summary>
///A test for Execute
///</summary>
[TestMethod()]
[DeploymentItem("WFUnitTest1.exe")]
[VerifyMocks()]
public void ExecuteTestNoMessage()
{
    WriteLineActivity2_Accessor target = new WriteLineActivity2_Accessor();
    ActivityExecutionContext executionContext = null; 
    ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
    ActivityExecutionStatus actual;

    actual = target.Execute(executionContext);

    Assert.AreEqual(expected, actual);
}

Still pretty simple because there is no interaction between the activity and the ActivityExecutionContext. But what happens the message is filled?

/// <summary>
///A test for Execute
///</summary>
[TestMethod()]
[DeploymentItem("WFUnitTest1.exe")]
[VerifyMocks()]
public void ExecuteTestWithMessage()
{
    WriteLineActivity2_Accessor target = new WriteLineActivity2_Accessor();
    ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
    ActivityExecutionStatus actual;

    ActivityExecutionContext executionContext = 
        RecorderManager.CreateMockedObject<ActivityExecutionContext>();
    WriteLineService2 service = 
        RecorderManager.CreateMockedObject<WriteLineService2>();

    using (RecordExpectations recorder = RecorderManager.StartRecording())
    {
        // Record methods here
        service.WriteLine(null);

        executionContext.GetService<WriteLineService2>();
        recorder.Return(service);
    }

    target.Message = "A message";
    actual = target.Execute(executionContext);

    Assert.AreEqual(expected, actual);
}

In this case we are mocking the ActivityExecutionContext using the RecorderManager.CreateMockedObject<ActivityExecutionContext>() statement. And this mock ActivityExecutionContext returns a mock WriteLineService2 when asked, pretty cool right? Big Smile

Now I won't claim to know every mocking framework out there but as far as I know TypeMock is the only one that can to this and create proper unit test for a custom workflow activity.

Enjoy!

www.TheProblemSolver.nl
http://wiki.WindowsWorkflowFoundation.eu

Published Mon, Jun 2 2008 17:17 by Maurice
Filed under: , , ,

Comments

# re: Unit testing custom workflow activities@ Wednesday, June 04, 2008 9:16 AM

i need ur assistance in my project writing

by emmanuel

# Unit testing asynchronous workflow activities@ Wednesday, June 11, 2008 3:56 AM

In a previous blog post I demonstrated how TypeMock allowed us to mock out the workflow runtime infrastructure

# Unit testing asynchronous workflow activities@ Wednesday, June 11, 2008 4:08 AM

In a previous blog post I demonstrated how TypeMock allowed us to mock out the workflow runtime infrastructure

# New and Notable 247@ Thursday, June 12, 2008 7:47 AM

Enjoying cup of Starbucks and Deep Purple Live in Stockholm 1970 (via Zune marketplace). WCF/WF My good

# Exploring Workflow Foundation Part 1: Custom Activities &laquo; Hungry for Knowledge@ Sunday, July 13, 2008 1:59 PM

Pingback from  Exploring Workflow Foundation Part 1: Custom Activities « Hungry for Knowledge

# Unit Testing Activities with Windows Workflow Foundation@ Wednesday, August 27, 2008 1:50 PM

The other day somebody asked me about unit testing activities without writing a test workflow. 

# New Virtual Lab - Using Test First Development with WF 3.5@ Monday, December 01, 2008 9:59 PM

I'm very pleased to announce the publication of a new WF 3.5 hands-on lab to the MSDN Virtual Labs site:

# New and Notable 247@ Tuesday, December 02, 2008 3:39 PM

Enjoying cup of Starbucks and Deep Purple Live in Stockholm 1970 (via Zune marketplace). WCF/WF My good friend Marjan has worked hard with us Influencers (I wish I wasn’t so busy) to come up with a stellar bunch of Webcasts! Must see! One of the

# Unit testing and Windows Workflow Foundation@ Wednesday, December 03, 2008 6:17 AM

A while a go I write some posts about unit testing Windows Workflow foundation Activities, you can find

# re: Unit testing custom workflow activities@ Monday, March 09, 2009 2:04 PM

Hi,

Your solution is to buy another software (TypeMock) to create a WWF Unit Test?

This are the software restriction for distributing it into your software:

www.typemock.com/eula.html

by Juancho