Testing a workflow

The Flower testing API provides means to run processes isolated in a unit-test environment. It is compatible with any unit-testing and mocking framework.

To test a workflow, create an empty unit-test project and add references to your workflow library and Flower.Testing.dll. Flower test scenarios are built by using Flower.Testing.WorkflowTest<> class. Typical test looks like this:

//Success test
new WorkflowTest<TestWorkflow>("Test case name")
    .UsingMemoryDirectory
    (
        dir => 
            ... //Directory initialization.
    )
    .Run
    (
        new MyWorkflow
        {
            //Process state initialization.
        }
    )
    .From("Start activity").To("End activity")
    .CheckBefore
    (
        "Some activity", 
        (client, process) => 
        {
            //Assertions.
        }
    )
    .CheckAfter
    (
        "Some activity", 
        (client, process) => 
        {
            //Assertions.
        }
    )
    .CheckAfterFinish
    (
        (client, process) => 
        {
            //Assertions.
        }
    )
    .Go(); //Executes the test.

//Failure test
new WorkflowTest<MyWorkflow>("Test case name")
    .UsingMemoryDirectory
    (
        dir => 
            ... //Directory initialization.
    )
    .Run
    (
        new MyWorkflow
        {
            //Process state initialization.
        }
    )
    .From("Start activity").To("End activity")
    .Expect<Exception>
    (
        "Some activity", 
        ex => true //Check if the exception is the one expected.
    )
    .Go(); //Executes the test.

The class WorkflowTest is a builder that you instantiate, use to construct a test scenario and call its Go method to execute the test.

For any test you must setup the directory and a process state.

Normally, in unit-tests you will use in-memory directory implementation set up by using directory builder. To do that, call UsingMemoryDirectory and provide lambda building the directory state you expect at the beginning of the test. However, you can use any other directory implementation specified via UsingDirectory method.

Also, you need to construct the process state and pass it to the Run method (that's why you need the process variables to be public).

By using methods From and To you can optionally specify the activity from which to start the test (inclusively) and the activity until which to run it (not inclusively). Activities are identified by names. It may be convenient to explicitly demarcate sections to test by using breakpoints.

You need to write assertions checking pre-conditions and post-conditions of activities within the test section. The methods CheckBefore and CheckAfter allows to perform checks around a named activity, CheckAfterFinish triggers checks after the last activity executed. Lambdas executing the assertions are provided with the Flower client and the process state at the moment of check.

There are two types of tests - those which test successful paths and those which test paths throwing exceptions. The difference between them is that the first perform only checks, the second have Expect call which states: "This activity must throw this kind of exception; if it doesn't or the exception is different, that's an error". If the activity thrown an exception and it matches the type of the Expect call, the lambda passed to the call is executed and the test passes if the lambda evaluates to true.

Finally, the Go method needs to be called to actually execute the test.

Using directory builder

Directory builder passed to UsingMemoryDirectory allows to construct a directory in a declarative way. A directory declaration looks like this:

dir
    .Root("/Sets/Shared")
        .Folder("MySets")
            .Set("MySet", typeof(MyMessage), 0).End()
        .End()
    .End()
    .Root("/Workflows")
        .Folder("MyWorkflows")
            .Workflow("MyWorkflow", typeof(MyWorkflow), null).End()
        .End()
    .End()

Each method of the builder (except End) opens a scope linked to a directory entry; End call terminates a scope. The first level calls are always Root calls. These calls specify an entry of the current scope among those which already exist in the directory.

The following entries exist by default:
  • /Assemblies (folder).
  • /Services (folder).
  • /Workflows (folder).
  • /Processes (folder).
  • /Processors (folder).
  • /Sets (folder).
  • /Sets/Local (folder).
  • /Sets/Shared (folder).
  • /Roles (folder).
  • /Roles/Application (role).
  • /Roles/Processor (role).
  • /Roles/Administrator (role).

Testing API also creates a processor /Processors/TestProcessor and a process to be tested in /Processors/TestProcessor/Pending, but you shouldn't rely on that.

Other methods of the builder each correspond to some directory entry type. The first argument is the name of the entry to create; the rest of the arguments are the attributes specific to the entry type.

To learn more about the directory builder, see:

Next

Flower testing API provides a way to run processes in isolation, but sometimes you may wish to test integration between applications, processes and services. For that you need to install Flower services locally and setup the directory. To learn how, see "Setting up a directory, a processor and deploying a workflow".

Last edited Apr 17, 2013 at 8:38 PM by dbratus, version 2

Comments

No comments yet.