Testing SharePoint Workflows using TypeMock Isolator

I have for a while been trying to test SharePoint workflows using TypeMock Isolator to mock out the SharePoint fixtures, I want to remove the dependency of having SharePoint on any test boxes where possible. I have at last got this working after getting a new version of TypeMock Isolator 5.3.0 + a fix from the very helpful team at TypeMock

My idea was to be able to build a workflow that could changed list item properties for a document e.g. the workflow could set a field called approved to true if certain criteria were met. Now as a MOSS2007 workflow is based on .NET WF I knew I could try to build upon the work I document in my previous post on TDD for WF.

My test system was as follows:

  1. I created a new SharePoint workflow, all it contained was a decision box that went down the true path if the document associated with the workflow has a title starting with the letter A
  2. In a coded action for the true path, I then set an approved property to true.

All very simple, but good enough for this test, the key methods are shown below

    private void IfTest(object sender, ConditionalEventArgs e)
       var currentItem = workflowProperties.Item; 
e.Result = currentItem.Title.StartsWith("A"); } private void TrueTask(object sender, EventArgs e) { var currentItem = workflowProperties.Item; currentItem["Approved"] = true.ToString(); currentItem.Update(); }

I then created a test using the same form I did for WF based testing, I think the comments cover the key points

        public void WorkFlowSwitchOnTitle_TitleStartsWithA_SetApprovelField()
            using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())

                // Create our fake workflow and items
                var fakeProperties = Isolate.Fake.Instance<SPWorkflowActivationProperties>(Members.ReturnRecursiveFakes);
                var fakeItem = Isolate.Fake.Instance<SPListItem>(Members.ReturnRecursiveFakes);

                var fakeField = Isolate.Fake.Instance<SPField>(Members.ReturnRecursiveFakes);
                fakeField.DefaultValue = false.ToString();
                Isolate.WhenCalled(() => fakeProperties.Item).WillReturn(fakeItem);
// setup the if test Isolate.WhenCalled(() => fakeItem.Title).WillReturn("ABC"); Isolate.WhenCalled(() => fakeItem["Approved"]).WillReturn(fakeField); // setup the workflow handling
AutoResetEvent waitHandle = new AutoResetEvent(false); workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { // don't put asserts here as will be in the wrong thread waitHandle.Set(); }; workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e) { // don't put asserts here as will be in the wrong thread waitHandle.Set(); }; // when this is called the constructor is called twice // the first time is for validation for the workflow in this appdomain see http://odetocode.com/Blogs/scott/archive/2006/03/30/3192.aspx // then the real construction is run, the problem is this double run means that the Isolate.Swap.NextInstance // fails as it attaches to the first validation create, not the second real one WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(SharePointWorkflow.Workflow1)); // SO for this reason we only get the swap after the first create has been done Isolate.Swap.NextInstance<SPWorkflowActivationProperties>().With(fakeProperties); // we then recreate the workflow again, this time it has already been validated so // the swap works instance = workflowRuntime.CreateWorkflow(typeof(SharePointWorkflow.Workflow1)); instance.Start(); waitHandle.WaitOne();
// wait for the workflow to complete and then check the method expected were called
Isolate.Verify.WasCalledWithExactArguments(() => fakeItem.Update()); Isolate.Verify.WasCalledWithExactArguments(() => fakeItem["Approved"] = "True"); } }

If you try this without the fix TypeMock provided me with, the two verifies will fail, I am told this is due to a threading issue. Interesting that this is SharePoint specific as the same basic method works OK for standard WF workflows.

I also understand this fix will be in the next TypeMock release, I will update this post when I know for sure