Build your own CAB #18: The Command Executor

EDIT:  Oops on my part.  This is effectively the Active Object pattern. 

 

The rest of the Build your own CAB series can be found at the table of contents.

 

There’s a thread on the altdotnet list this morning about how to unit test background operations originating from a screen Presenter.  I have a strategy for that in StoryTeller that I think has worked out quite well.  Instead of creating a background worker or thread directly in a screen presenter, I run all background actions through what I call a “Command Executor.”  When a presenter or a menu command needs to run some sort of asynchronous command I have that class delegate to an instance of an ICommandExecutor interface.  The interface for the ICommandExecutor in StoryTeller is shown below:

    public interface ICommandExecutor : IStartable
{
void Stop();
void ExecuteCommandWithCallback(ICommand command, ICommand callback);
void ExecuteCommand(ICommand command);
}

You’ll see that the ICommandExecutor takes in the inevitable ICommand interface because this was written in .Net 1.1 and ported upwards later.  I think for my new project I’m going to something more like this to take advantage of lambda expressions instead of cluttering up the code with extraneous class definitions.

public interface ICommandExecutor
{
void Execute(Action action);
void ExecuteWithCallback(Action action, Action callback);
}

Now, why would you do this?  Using a BackgroundWorker or a background thread isn’t that hard, but:

  1. The threading code is noise code that distracts the reader from the real meaning of the code.  The CommandExecutor pattern let’s us change the code semantics inside the Presenter into merely a call to “run this in the background.”
  2. It’s potentially a lot of repetitive code to set up threads or bootstrap a BackgroundWorker.  You have to remember to do the thread synchronization every single time.  By using a Command Executor you can write the multi threaded code once and only once, including the work to synchronize threads (I use a SynchronizationContext in my CommandExecutor for the callbacks.  No need for AOP black magic whatsoever).
  3. You do NOT want any important behavioral or business logic code to be coupled to the UI machinery.  I see a lot of teams absolutely screw themselves over by embedding logic directly into the DoWork event of a BackgroundWorker, rendering that code effectively impossible to reuse or unit test.  The CommandExecutor will help push teams to separate the behavioral logic away from threading infrastructure to make that code both easier to test and reuse.
  4. The background threading is rougher in unit testing.  Not impossible, but it does add some significant overhead to the unit testing effort.

Let’s take the testing angle first.  The original problem was how to be able to unit test the background operation.  The easiest way is to simply turn the asynchronous behavior completely off by substituting in a synchronous command executor in the unit tests like this:

    public class SynchronousCommandExecutor : ICommandExecutor
{
public void Stop()
{
// no-op;
}

public void ExecuteCommandWithCallback(ICommand command, ICommand callback)
{
command.Execute();
callback.Execute();
}

public void ExecuteCommand(ICommand command)
{
command.Execute();
}

public void Start()
{
// no-op;
}
}

Now you can simply run your unit test and test the asynchronous behavior without having to setup ManualResetEvent’s or other thread synchronization machinery.  In StoryTeller I usually just test that the proper ICommand message was sent to the ICommandExecutor and call it good enough.  I’ll then turn around and test the ICommand.Execute() method in isolation in another unit test.

So far, I’ve found this pattern pretty well eliminates all of my unit testing problems with background operations.  For integration testing one of your challenges for testing a user interface is asynchronous events.  One of the solutions I’ve found to this problem is to route all background operations through some sort of CommandExecutor so that the test harness has a single place to watch in order to synchronize the test script with the user interface. 

 

 

Anyway, I banged this out fast.  Any thoughts?

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Build your own CAB. Bookmark the permalink. Follow any comments here with the RSS feed for this post.