다음을 통해 공유


Mocks for Unit Testing

This post is not exactly related to Designer, but something I am looking closely into nowadays. Recently a customer asked, how can he replace one of his long running activities with a different mock activity so that he can still unit test his workflow/composite activity logic.

Another scenario would be, how to replace an activity which queries a live Db( or does some other Aysnc work) with a test activity which can query a test Db or even a file during testing.

One way is to load the activity in the designer and then use the MorphHelper class to replace the long running activity with a mock activity. You can then use wd.Flush(wd is an object of type WorkflowDesigner) and then use the wd.Text to save the updated Xaml into a different file to finally unit test the workflow/composite activity.

The cleaner way though, is something that one of the Devs in our team, Dan Glick, suggested.  Instead of loading through the designer and going about multiple steps, the developer/tester can use a custom XamlSchemaContext.

For example: Suppose you want to replace a Prompt activity with an Action activity. Also, to add some complexity to the complexity, assume that Prompt activity has an InArgument<string> UserResponse. However Action has an InArgument<string> but with a different name – UserAction. Meaning, there is no 1:1 correspondence between the arguments/properties between the two activities.

To achieve this replacement, we can write something as follows:

     public class MySchemaContext : XamlSchemaContext
    {
        public override XamlType GetXamlType(Type type)
        {
            if (type == typeof(Prompt))
            {
                return new myXamlType(typeof(CustomActivities.Action), this);
                //return base.GetXamlType(typeof(CustomActivities.Action));
            }
            else
            {
               return base.GetXamlType(type);
            }
        }
    }

    public class myXamlType : XamlType
    {
       

        public myXamlType(Type t, XamlSchemaContext schemaContext):base(t, schemaContext)
        {
            

        }

        protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck)
        {
            //return base.LookupMember(name, skipReadOnlyCheck);
            if (name == "UserResponse")
            {
                return base.LookupMember("UserAction", skipReadOnlyCheck);
            }
            else
            {
                return base.LookupMember(name, skipReadOnlyCheck);
            }

        }

    }

With these two helper classes in place, we now get the new activity object tree that can then be passed to a runtime host and thus unit test the workflow/activity successfully.

 Activity act = ActivityXamlServices.Load(new XamlXmlReader("myFile.Xaml", new MySchemaContext()));

Hope this helps!

Thanks,

Kushal.

Comments

  • Anonymous
    April 22, 2010
    You can also do some interesting stuff if you use the XmlnsDefinitionAttribute to map a namespace against a custom URI.  Using this in combination with a XamlSchemaContext, you can do some interesting swapping between activities used in the designer and those used at runtime.

  • Anonymous
    January 25, 2011
    This ability is now included in Microsoft.Activities.UnitTesting,XamlInjector For an example see channel9.msdn.com/.../endpointtv-How-to-Unit-Test-a-WF4-Workflow-that-calls-a-WCF-Service