Unit Testing Workflow Activities with Moles

Unit Testing Workflow Activities with Moles

  • Comments 6
  • The Why: True unit testing of Workflow Activities is a pain if not impossible.
  • The How: Use Moles CLR Detour technologies developed by Microsoft Research to isolate the system under test.
  • The What: A sample C# project that showcases a handful of core Workflow unit test scenarios.

I’ve been working with Workflow Foundation for a few year now and I’ve become a huge fan of the technology. Working in the enterprise software space I find it to be a natural fit as a solution for a great deal of scenarios. But (there is always a but) if you have ever been charged with the task of unit testing workflow activities or practice TDD you are probably familiar with how frustrating it can be. For what ever reason the Workflow team wasn’t overly concerned with unit test-ability and in my  opinion were overly aggressive in marking constructors as internal and the like. Unit testing is primarily concerned with testing the smallest unit of functionality possible which is typically a single method. With Workflow Activities this is very difficult. For example you may want to unit test the Method “Execute” on your CodeActivity which takes an instance of CodeActivityContext, but the constructor is marked internal and it doesn’t implement an interface that we can leverage to create Stand-In classes. The usage of interfaces to create and pass in Stand-In classes is my preferred method of isolating the system under test to small testable chunks so I find this especially troublesome. But since that options isn’t available to us when we are working with Activities we must do something else.  One option I’ve found useful is an approach the uses Moles from Microsoft Research that is based on CLR Detours.

Moles

Moles is part of the Pex & Moles project from MSR that leverages a concept called Detours. a Detour is when the memory location for a specific method on a specific instance is updated to point to a mock class with the same method signature. In other words this gives us a back door to wiring in mock functionality. Below is a brief example of how Moles works but a full explanation  of Moles is out of scope for this post so if you are new to Moles head on over to MSR and check out their demos and How-Tos.

y2kbug

 

Moles and Workflow Activities

Below is a diagram showing the basic architecture of how I’ve chosen to unit test activities. Starting from the unit test, Moles and a Test Stand-In classes are used to manipulate and confine the system under test (the Workflow Activity). Moles is used to wire in delegates that allows us to eliminate pieces of typical Activity execution that are not necessary parts of our test such as getting values from a InArgument which normally incorporates complex WF infrastructure. The test Stand-In is a simple component that replaces production classes with simplified test versions that provide test hooks and very predictable behavior. You’ll also see the use of test shims that expose protected members or my activities. I’ve chosen to use a derived class which uses the new keyword to create a public version of Execute which in turn calls the base version of Execute. You can use shadowing Accessors provided by the unit test wizards but I prefer the more explicit form of a derived class for the sake of a demo. Below is a diagram that shows the architecture of these unit tests and how a combinations of Test Stan-In classes and Moles allows us to focus the tests to a minimal amount of code.

image

Let’s dig into a Workflow specific use of Moles. Below are a few code samples of unit tests using Moles. You can find a link at the bottom of this post to the complete Visual Studio 2010 solution if you want more details about implementation.

Testing Execute on SimpleActivity

In this example we are testing SimpleActivity.Execute(CodeActivityContext context).  As part of Execute a CustomTrackingRecord is tracked and the same integer that is passed in to the InArgument<int> SomeInteger is returned. Both of these operations require the CodeActivityContext which we will need Moles to instantiate as Moles has the ability to create instances of classes even when their constructors are marked internal. Below is the code snippet for the entire unit test. After which each section is discussed in detail.

        /// <summary>
        ///A test for Execute
        ///</summary>
        [TestMethod()]
        [DeploymentItem("SampleActivities.dll")]
        [HostType("Moles")]
        public void ShouldReturnInt_WhenExecute_GivenInteger()
        {
            // Arrange
            int expected = 10;

            /// we need to create a moled CodeActivityContext so we can 
/// get an instance from the
Instance property of the moled
/// context as all constructors are marked as internal.
MCodeActivityContext moledContext = new MCodeActivityContext(); moledContext.TrackCustomTrackingRecord = (record) => { // do nothing }; /// We need to create a moled InArgument<int> so we can setup
/// a delegate to return our expected value without using
/// relying on any of the complexities of the WF runtime. MInArgument01<int> moledInArg = new MInArgument01<int>(); moledInArg.GetActivityContext = (ContextBoundObject => { return expected; }); TestableSimpleActivity target = new TestableSimpleActivity(); target.SomeInteger = moledInArg.Instance; // Act int actual = target.Execute(moledContext.Instance); //Assert Assert.AreEqual<int>(expected, actual); }

 

In the // Arrange section of the unit test two moles are created to handle various operations that we can’t create test Stand-Ins for. First we create a mole for the CodeActivityContext using MCodeActivityContext . Then we wire in the delegate for MCodeActivityContext .Track to MCodeActivityContext .TrackCustomTrackingRecord . This is the delegate that will serve as the Detour for the Track method. In other words this is the code that will be executed when Track is called on CodeActivityContext. In this detour delegate we don’t do any asserts but we could verify that the track call is tracking the correct data.

Next we wire in a delegate for InArgument<int>.Get. We can instantiate an InArgument but the Get call requires additional WF infrastructure that can’t reasonably reproduced outside of the WF Runtime so we detour it to get the simple behavior we want which is to just return the value expected.

Once our detours are setup we create an instance of the system under test using the Test Shim TestableSimpleActivity  which, as discussed before,  is simply a class that exposes protected members. Once we have our target object I set the SomeInteger property using the Instance property on our Mole object. We have to use the instance of InArgument<int> provided by the moled object as this is the instance that will be associated with our detours. If I just created and used another instance of InArgument<int> our detours would be ignored.

After we have the target object setup Execute is called using the instance of CodeActivityContext that is associated with our detours provided by the mole mole object moledContext.

Testing Asynchronous Execute on ComplexActivity

In the previous example I showed how to unit test the Execute method of a basic CodeActivityHowever the situation is rarely that simple in the real world. To showcase unit testing more complex activities I’ve created an activity that uses asynchronous Workflow concepts. There are several ways to create asynchronous activities. I’ve chosen to start with NativeActivity as this allows me to cover the use of bookmarks. In this unit test we are testing Execute again but this time it is an asynchronous operation and it creates a new thread using System.Task so there are a few extra steps. For example an external service is responsible for doing the actual work.

        [TestMethod]
        [HostType("Moles")]
        public void ShouldDoWork_WhenExecute_Moled()
        {
            //Arrange
            //Create test fixture version of target type to expose protected members.
            TestableComplexActivity target = new TestableComplexActivity();
            //Create a stand-in version of IWorkerService with my test hooks.
            TestWorkerService testSvc = new TestWorkerService();
            BookmarkOptions actualBmOptions = BookmarkOptions.None; 
//Create a moled NativeActivityContext to get access to an instance. //And to set a Detour for CreateBookmark MNativeActivityContext moledContext = new MNativeActivityContext(); moledContext.CreateBookmarkStringBookmarkCallbackBookmarkOptions
= (bmName, bmCallback, bmOptions) => { actualBmOptions = bmOptions; return new Bookmark(bmName); }; //Create a moled ActivityContext to setup a detour for
//
GetExtention<T>(Type) MActivityContext moledActContext =
new MActivityContext(moledContext.Instance); moledActContext.GetExtension<IWorkerService>( new MolesDelegates.Func<IWorkerService>(() => testSvc) ); bool actual = false; //Act target.Execute(moledContext); //blocks until worker thread completes. actual = testSvc.WaitForWorkToComplete(); //Assert Assert.AreEqual<bool>(true, actual); Assert.AreEqual<BookmarkOptions>(BookmarkOptions.MultipleResume,
actualBmOptions); }

To help constrain the test to just the Execute method I’ve created a test Stand-In version of the IWorkerService extension service called TestWorkerService to synchronize the threads for me and give me access to the data being passed into the service by Execute. This is an example of leveraging interfaces for the sake of testability via Stand-Ins. Another aspect that is new to this scenario is working with bookmarks. Bookmarks are a key component to asynchronous activities and are used to indicate where the workflow is suppose to resume execution upon completion of some external process. To accommodate this in our unit test we have to setup a Moles delegate on the Moles version of NativeActivityContext.CreateBookmark method. I use the Moles delegate to  capture the BookmarkOptions value so I can validate that against an expected value using an Assert later in the unit test. Another addition to this scenario is providing a MolesDelegates.Func to handle access to the test extension service when the Execute method calls GetExtension<IWorkerService>().  Finally the last piece that is new to this scenario is the synchronization.

In the //Act section of the unit test you will notice a call to WaitForWorkToComplete on the instance of TestWorkerService. This call blocks until a WaitHandle is signaled by the new thread spawned by Execute and returns the value passed into the worker services' DoWork method. Since the behavior of the “production” worker service is simply to return the value passed in to the DoWork method the test Stand-In emulates the same behavior.

Sample Solution

The sample solution contains a few more unit test scenarios not covered here.  Below is a list of additional scenarios contained in the sample project.

  • A test for the Abort method
  • A test for removing MultipleResume bookmarks within bookmark callback methods
  • A test for not removing MultipleResume bookmarks  when asynchronous process fails.
  • A test that verifies tracking data.

 

Complete sample code solution (VS 2010)UnitTestingActivitiesWithMoles.zip

 

  • I would rather use ron jacobs workflow testing library. Let's you easily test bookmarks etc.

    Daniel

  • Thanks for the tip. I hadn’t seen Ron’s stuff until you mentioned it. I’ll have to give it a try. And while I've never used Ron's stuff, from looking at the source code it doesn't look like true unit testing as the WorkflowApplicationTest is still executing the workflow within the workflow runtime using System.Activities.WorkflowApplication. Meaning tests using this approach are technically more like an integration test. I’ve done plenty of WF testing using a similar approach with good results so it’s probably more of a style / preference decision.

  • Hy

    For me it is not a matter style or preference. In my point of view is what you are doing is reproducing the behavior of the workflow runtime with pex and moles. I generally tend to think that this is not really desired. You should be able to rely on the workflow runtime. What you want to tests is the execution logic of you activity. That's why I only mock the extensions and the workflow arguments (if external dependencies) and then use a real workflow invoker to invoke the activity. I can show you a sample here which I wrote for the ninject WF extension (which I wrote ;)):

    github.com/.../Usage

    Don't get me wrong I don't want to blame your example. Just want to have a interesting discussion ;)

    Daniel

  • I see your point, you certainly can rely on the workflow runtime to behave in a consistent manner, and that form of testing (integration / component testing) is very valuable.  However ultimately it isn't true unit testing as too much complexity is involved in test. A unit test is typically going to execute a single public method of production code. Whereas executing an activity as a Workflow Instance incorporates many lines of production code (WF runtime). Adhering to a strict definition of a unit test is valuable when engaging in TDD/Agile as the volume of unit tests is high where execution time and test leverage needs to be minimized. This is especially the case if Gated Check-ins is being used.

    Wikipedia has a nice article on the definition of a Unit Test:

    In computer programming, unit testing is a method by which individual units of source code are tested to determine if they are fit for use. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual function or procedure. In object-oriented programming a unit is usually a method.[citation needed] Unit tests are created by programmers or occasionally by white box testers during the development process.

    Ideally, each test case is independent from the others: substitutes like method stubs, mock objects,[1] fakes and test harnesses can be used to assist testing a module in isolation. Unit tests are typically written and run by software developers to ensure that code meets its design and behaves as intended. Its implementation can vary from being very manual (pencil and paper) to being formalized as part of build automation.

    en.wikipedia.org/.../Unit_test

  • Nice point. I definitely see your point about speed and performance. Although for simple tests we used the WorkflowInvoker to host the activity. This went really smooth. If you want and can give you the timing behavior of all our activity related tests...

    Daniel

  • Thanks for a really great demo. I find no other examples of using Moles with WF4.

    I am making progress integrating it into my solution, but am stuck. Among other tings, I have successfully moled an OutArgument. But I can not set that value inside the Execute function (while testing) since I have not moled context.SetValue.

    I dies here:

               context.SetValue(OutLetter, _outLetterEntity);

    where OutLetter is the moled property, and _outLetterEntity is a local copy of the same type.

    Here's the exception:

    ActivityContext.SetValue(OutArgument`1<!!0>, !!0) was not moled.

    Looking inside the metadata for MCodeActivityContext I see nothing that resembles SetValue. Any suggestions?

Page 1 of 1 (6 items)
Leave a Comment
  • Please add 4 and 8 and type the answer here:
  • Post