On Monday I gave a quick overview and demo of the automation framework.  I had prepared a handful of command objects to drive features of the system that were simple to implement, but are cumbersome manual operations:

  • creating new user accounts;
  • resetting user credentials;
  • validating user credentials;
  • depositing to and withdrawing from a user's account;
  • querying the user's current balance;

I showed off the commands in an interactive PowerShell session that manipulated a live production system.  The interactive demo culminated in me creating 100 new user accounts on a production system, giving each a $500 deposit, in a one-liner:

@( 0..99 ) | % { "p" + $_ } | new-player | new-deposit 500; 

Using our standard production tools, this would have taken hours to accomplish and left plenty of room for user error; the script ran in under 30 seconds and produced zero defects.

I then re-used the same commands in several FitNesse fixtures, explaining how we could drive a lot of our testing effort using table-driven examples.  The reception was very positive from pigs and chickens alike, which made me a very happy camper.

One of the design aspects of the framework that went over well was that each command object encapsulated an atomic unit of work on our system - simple, a dozen or so lines of effort with clear inputs and outputs and a single goal.  These units could be combined to create complex behavior very quickly by compositing and chaining command objects together.

An Example of Compositing

Let's say you want to encapsulate the task of resetting user credentials, and let's say that process is comprised of the following atomic steps:

  1. Locate the user account from the data store;
  2. Acquire an authorization ticket to manage the user;
  3. Reset the user credentials using the ticket;
  4. Verify the new user credentials.

Some of these steps are dependent on others, but each step represents a reusable unit of work performed by the system.  E.g., step 2 will need to be repeated every time I want to touch a user account, not just when changing user credentials.  It would be good to encapsulate these atomic units as individual command objects so I can reuse them:

public interface ICommand
{
    bool Execute();
}

public class LoadUserAccountCommand : ICommand
{
    public bool Execute()
    {
        // ...
    }
}

public class AcquireAuthorizationTicketCommand : ICommand
{
    public bool Execute()
    {
        // ...
    }
}

public class SetUserCredentialsCommand : ICommand
{
    public bool Execute()   
    {
        // ...
    }
}

public class VerifyUserCredentialsCommand : ICommand
{
    public bool Execute()   
    {
        // ...
    }
}

At the same time, I don't want to have to remember to execute four commands every time I need to perform a simple system task.  So I'll encapsulate that too, using a Composite pattern:

public CompositeCommand : List< ICommand >, ICommand
{
    public CompositeCommand( params ICommand[] cmds ) : base( cmds )
    {
    }
   
    public bool Execute()
    {
        foreach( var cmd in this )
        {
            if( ! cmd.Execute() )
            {
                // halt on first failed command
                return false;
            }           
        }
        return true;
    }
}

public class ResetUserCredentialsCommand : CompositeCommand
{
    public ResetUserCredentialsCommand()
        : base(
            new LoadUserAccountCommand(),
            new AcquireAuthorizationTicketCommand(),
            new SetUserCredentialsCommand(),
            new VerifyUserCredentialsCommand()
        )
    {
    }
}

In essence, a composite command allows me to treat a sequential list of commands as if it were a single command.  The composite is a list of ICommand objects, and it supports the ICommand contract so it looks like any other command object to the framework.  The implementation of the Execute() method simply iterates over each command in the composite, executing each in turn until a command fails (returns false) or the end of the command collection is reached.

Sidebar: if you want to get sticky, in a true composite pattern the iteration over the child ICommand objects would not be contingent on the result of the previous command's execution.  That makes this more of a strategy than composite methinks.  However, I'm not that kind of pattern dude anymore and I think the intent is enough to call it a composite.  If you're a member of the pattern police and want to bust my chops, please leave a comment.

An Example of Chaining

Take another example: I need an account for a specific user.  That user may or may not exist yet, I don't care - I just want to run my test fixture.  In this case, I have two steps:

  1. Load an existing user account;
  2. Create a new user account.

Unlike the composite, these actions are not meant to be run in sequence - step two should only execute if step 1 fails to find an existing user.  In these cases, I use a chain of commands to encapsulate the logic:

public ChainOfCommand : List< ICommand >, ICommand
{
    public ChainOfCommand( params ICommand[] cmds ) : base( cmds )
    {
    }
    
    public bool Execute()
    {
        foreach( var cmd in this )
        {
            if( cmd.Execute() )
            {
                // halt on first successful command
                return true;
            }            
        }
        return false;
    }
}

public class LoadOrCreateUserAccountCommand : ChainOfCommand
{
    public LoadOrCreateUserAccountCommand()
        : base(
            new LoadUserAccountCommand(),
            new CreateUserAccountCommand()
        )
    {
    }
}

The code is almost identical to the CompositeCommand class.  The key difference is in the ChainOfCommand.Execute() method - where CompositeCommand executes each child command until one fails, ChainOfCommand executes each child command until one succeeds.  For example, when the LoadOrCreateUserAccountCommand is executed, no new user account is created if one can be loaded.

Again, sticklers will point out that this isn't true to a chain of responsibility pattern, and I'm ok with that.  It's just damn useful, whatever you call it.

Seeking Advice

My next post on this spike will be a request for advice, so please tune in ...