WF - Unit Testing: Simple approach to Activity testing and improving the XOML loader.

Usually I agree with Damir and his excellent posts. However, I cannot agree 100% with his post about "WF - Unit Testing: How to create Activity Type from XOML?".

When not reading it carefully, it can be misunderstood in a way that the actual "Activity Type" is created from XOML.
What is shown in the example is not an actual type being created from XOML, but rather an extremely small workflow loading the type.

The type is still defined in an external assembly, during development most likely referenced as a project.
So usually, for being able to debug, I prefer to test/debug single activities this way, since I have the assembly with the type referenced anyway:

public bool RunActivity<ACTIVITY>(
                Dictionary<string, object> namedArgumentValues) 
                    where ACTIVITY : Activity

{

    using (WorkflowRuntime wr = new WorkflowRuntime())

    {

        ManualWorkflowSchedulerService mwfs
                          = new ManualWorkflowSchedulerService();

        wr.AddService(mwfs);

        WorkflowInstance wi = wr.CreateWorkflow(typeof(ACTIVITY),
                                 namedArgumentValues);

        wi.Start();

        return(mwfs.RunWorkflow(wi.InstanceId));

    }

}

 

[TestMethod]

public void TestMethod00()

{

    Dictionary<string, object> namedArgumentValues 
                               = new Dictionary<string, object>();

    namedArgumentValues.Add("WorkflowArgument", "test");   

    bool isRunning
         =RunActivity<MyActivityNamespace.MyActivity>(
                                          namedArgumentValues);

    Assert.IsTrue(isRunning, "WorkflowActivity cannot be run.");

}



Now back to XOML-loading: Let me give some errata for Damir's example that might confuse some people. "activity" is defined twice and if you fix this in a way that you will actually take the type "MyActivity" for activity in this method, then the example would be trivial, because you know your type and do not need to get it from XOML.

 

[TestMethod]

public void TestMethod00()

{

    Activity activity = GetActivityTypeFromXoml(m_Xoml);

 

    WorkflowRuntime runtime = new WorkflowRuntime();

    ManualWorkflowSchedulerService scheduler =

    new ManualWorkflowSchedulerService();

 

    runtime.AddService(scheduler);

 

    //MyActivity activity = new MyActivity();

    WorkflowInstance instance;

    Dictionary<string, object> props
                               = new Dictionary<string, object>();

 

    props.Add("WorkflowArgument", "test");

    instance = runtime.CreateWorkflow(activity.GetType(),
                                      props, Guid.NewGuid());

    instance.Start();

 

    scheduler.RunWorkflow(instance.InstanceId);

}

 

And a small tip. If you are lazy, execution speed is not an issue and you do not want to keep track of the names that the assemblies have and since the assembly containing the activity is referenced from the test-project, try this:

private void AddReferencedAssemblies(TypeProvider typeProvider)

{

    Assembly thisAssembly;

    thisAssembly = Assembly.GetAssembly(this.GetType());

    foreach (AssemblyName assemblyName in
            
thisAssembly.GetReferencedAssemblies())

    {

        typeProvider.AddAssembly(Assembly.Load(assemblyName));

    }

}

So, my full version of this workflow-loader is:

string m_Xoml = @"<ns:MyActivity xmlns:ns="""
       +@"http://daenet.eu/workflow.activities""></ns:MyActivity>"
;

 

private void AddReferencedAssemblies(TypeProvider typeProvider)

{

    Assembly thisAssembly;

    thisAssembly = Assembly.GetAssembly(this.GetType());

    foreach (AssemblyName assemblyName
             in thisAssembly.GetReferencedAssemblies())

    {

        typeProvider.AddAssembly(Assembly.Load(assemblyName));

    }

}

 

private Activity GetActivityTypeFromXoml(XmlReader reader)

{

    using (WorkflowRuntime runtime = new WorkflowRuntime())

    {

        using (TypeProvider typeProvider = new TypeProvider(
                                               runtime))

        {

            AddReferencedAssemblies(typeProvider);

 

            using (ServiceContainer container = new 
                                        ServiceContainer
())

            {

                container.AddService(typeof(ITypeProvider),
                                     typeProvider);

 

                DesignerSerializationManager manager =

                new DesignerSerializationManager(container);

                manager.CreateSession();

 

                WorkflowMarkupSerializationManager 
                    serializationManager =

                new WorkflowMarkupSerializationManager(manager);

 

                Activity activity =

                new WorkflowMarkupSerializer().Deserialize(
                                           serializationManager,
                                           reader)
as Activity;

                return activity;

            }

        }

    }

}

 

public bool ExecuteWorkflowFromXoml(XmlReader reader, 
                Dictionary<string, object> namedArgumentValues)

{

    using (WorkflowRuntime runtime = new WorkflowRuntime())

    {

        using (Activity activity = GetActivityTypeFromXoml(
                                       reader))

        {

            ManualWorkflowSchedulerService scheduler =

            new ManualWorkflowSchedulerService();

            runtime.AddService(scheduler);

 

            WorkflowInstance instance = runtime.CreateWorkflow(
                                            activity.GetType(),
                                            namedArgumentValues);

            instance.Start();

            return (scheduler.RunWorkflow(instance.InstanceId));

        }

    }

}

 

[TestMethod]

public void TestMethod00()

{

    Dictionary<string, object> namedArgumentValues
                              = new Dictionary<string
                                               object>();

    namedArgumentValues.Add("WorkflowArgument", "test");

    bool isRunning=ExecuteWorkflowFromXoml(
                       XmlReader.Create(new StringReader(
                                                m_Xoml)),
                                        namedArgumentValues);

    Assert.IsTrue(isRunning, "WorkflowActivity cannot be run.");

}



Note:
You will not be able to parse the XOML defining the actual activity from your designer this way.
For that purpose, you would look into ActivityMarkupSerializer:

ActivityMarkupSerializer ams = new ActivityMarkupSerializer();
...

using (Activity ac = (Activity)ams.Deserialize(
                                   XmlReader.Create(stream)))
...

But do not expect to be able to execute that activity directly.


Posted Aug 01 2007, 09:21 PM by Andreas Erben

Comments

K. Scott Allen wrote A Few Interesting Windows Workflow Links
on 01-21-2008 5:53

Igor emailed me for my thoughts about his SMTP server built with Windows Workflow. A link for the source...

BusinessRx Reading List wrote A Few Interesting Windows Workflow Links
on 01-21-2008 6:00

Igor emailed me for my thoughts about his SMTP server built with Windows Workflow . A link for the source

... wrote re: WF - Unit Testing: Simple approach to Activity testing and improving the XOML loader.
on 01-30-2009 7:08

Lovely. Great site.

... wrote re: WF - Unit Testing: Simple approach to Activity testing and improving the XOML loader.
on 01-30-2009 7:09

Lovely. Great site.

developers.de is a .Net Community Blog powered by daenet GmbH.