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 [:)]


 


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? [:D]


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

2 thoughts on “Unit testing custom workflow activities

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>