Who Ya Gonna Call?

Once you create your Logical Functional Model the other pieces can follow in any order immediately as chunks of your feature and LFM are defined. I'll follow the most typical pattern here, which brings us to Execution Behaviors.

When you first define your LFM you focus on what the user is doing not how they are doing it. The next step - defining Execution Behaviors - starts to drill down into the how. For each user action you defined in your LFM, determine every possible way the user can invoke that action. For example, every action in Surveyor can be invoked by both the mouse and the keyboard; to indicate they are done with the survey (to nest examples), the user can:

  • click the Done button with their mouse
  • press the Tab key until the Done button is highlighted and then press the spacebar to invoke it

These two possibilities are pretty much it in Surveyor, but the more complicated your application the more execution variants you are likely to have. (To wit: how many different ways can you create a new document in your word processor?)

After you have identified the various Execution Behaviors for an action you organize them in two ways:

  • Into semantically equivalent groups. It is vital that each Execution Behavior in a group has exactly the same end result as each other Execution Behavior in the group. Remember that the whole point of Execution Behaviors is that the test case doesn't care which one will be selected; if two Execution Behaviors have different results which one is selected obviously will matter.
  • Into an execution method graph. This graph is tiny for Surveyor - simply two sibling leaf nodes:
    • using the keyboard
    • using the mouse The example of creating a new document is slightly more complex:
    • using the menu
      • navigating the menu using the keyboard
      • navigating the menu using the mouse
    • using the toolbar
      • using the mouse
    • using the object model

Each of these groups translates directly to a Composite Execution Behavior / child Execution Behavior relationship:

  • Each semantically equivalent group requires a separate top-level Composite Execution Behavior.
  • Each branch in the execution method graph defines a Composite Execution Behavior; each child of a branch defines its child Execution Behaviors.

Now you're ready to turn your LFM stubs into Composite Execution Behaviors and stub out the corresponding child Execution Behaviors. Surveyor's Logical.Navigation.IndicateAmDone method

public static void IndicateAmDone() { }

becomes

[CompositeExecutionBehavior("IndicateAmDoneUsingMouse", "IndicateAmDoneUsingKeyboard")]
public static void IndicateAmDone()
{
Delegate chosenChild = ExecutionBehaviorManager.ChooseAny(Logical.Navigation.IndicateAmDone);
chosenChild();
}

public static void IndicateAmDoneUsingMouse() { }
public static void IndicateAmDoneUsingKeyboard() { }

Execution Behaviors for creating a new document might code up thusly:
[CompositeExecutionBehavior("CreateNewDocumentUsingMenu", "CreateNewDocumentUsingToolbar", "CreateNewDocumentUsingObjectModel")]
public static void CreateNewDocument()
{
Delegate chosenChild = ExecutionBehaviorManager.ChooseAny(Logical.Documents.CreateNewDocument);
chosenChild();
}

[CompositeExecutionBehavior("CreateNewDocumentUsingMenuUsingKeyboard", "CreateNewDocumentUsingMenuUsingMouse")]
public static void CreateNewDocumentUsingMenu()
{
Delegate chosenChild = ExecutionBehaviorManager.ChooseAny(Logical.Documents.CreateNewDocumentUsingMenu);
chosenChild();
}
public static void CreateNewDocumentUsingMenuUsingKeyboard() { }
public static void CreateNewDocumentUsingMenuUsingMouse() { }

[CompositeExecutionBehavior("CreateNewDocumentUsingToolbarUsingMouse")]
public static void CreateNewDocumentUsingToolbar()
{
Delegate chosenChild = ExecutionBehaviorManager.ChooseAny(Logical.Documents.CreateNewDocumentUsingToolbar);
chosenChild();
}
public static void CreateNewDocumentUsingToolbarUsingMouse() { }

public static void CreateNewDocumentUsingObjectModel() { }

Test cases now have a variety of options when they need to create a new document:

  • If they don't at all care how the action is invoked, they simply call the top level Composite Execution Behavior (i.e., CreateNewDocument).
  • If they partially care how the action is invoked, they call one of the intermediate Composite Execution Behaviors (e.g., CreateNewDocumentUsingMenu).
  • If the completely care how the action is invoked, they call the corresponding child Execution Behavior (e.g., CreateNewDocumentUsingMenuUsingMouse).

This is why I made the "using toolbar" variant a Composite even though it only has a single child - doing so allows test cases to state whether they care how the toolbar button is invoked.

With Execution Behaviors integrated into your LFM you get automatic variation over the full set of execution methods, which lets you write fewer test cases (since you no longer need an explicit test case for each different implementation). Individual test cases however have the flexibility to use a specific implementation, so regression tests and such can still be written against the LFM. This is one case where you really can have it both ways!

*** Want a fun job on a great team? I need a tester! Interested? Let's talk: michhu at microsoft dot com. Great coding skills required.