/
Workflow Validation (Szenario Fixture)

Workflow Validation (Szenario Fixture)

 

Definition

Definition

The ScenarioInterpreter is used to express interactions with the system under development that must be performed in a particular order. This form of specification provides information about the business flow.

When a sequence of action is executed,  confirms that each action has successfully been performed.

 


 

ScenarioIdentification of the set of rule
Action 1

 

Action 2

...
Action i

 


 

  • As for all other interpreters, the first row of the ScenarioInterpreter specifies the name of the interpreter and the name of the sequence of actions to be tested. What makes the ScenarioInterpreter particular is that it only has to be defined once for all the sequences of actions expressed in a page. Obviously, the ScenarioInterpreter must be define before any sequence of actions.
  • The ScenarioInterpreter may also be expressed in Bullet List form or Number List form.

Coloring

 will visually show the test result by coloring a complete row or words inside the row:

Green

When the action has been executed successfully,  colors the row or words inside the row in green.

Red

If the action execution has failed,  colors the the row or words inside the row in red.

YELLOW

If the system encounters an execution error, the row is colored yellow and  provides information about the error.

Grey

When the action has been executed successfully,  will display the returned value in gray.

 Writing fixtures for Scenario tables

As we've seen in the Scenario definition, a sequence of tables is used to express a business flow in the application under development.

When running the table,  uses a fixture to mediate between the example expressed in the sequence of tables and the system under development. The fixture code defines how the specific actions are mapped to the application code.

This page shows the fixture code that supports the examples introduced in the Writing a Scenario specification.

Fixture for Bank

Consider the first example of business flow described in Writing a Scenario specification, shown again below.

scenariobank
open checking account 12345-67890 under the name of Spongebob Squarepants
verify that balance of account 12345-67890 is $0.00
end

The first table indicates to use a ScenarioInterpreter, which handles a business flow expressed as a sequence of tables. The fixture Bank will do the mediation with the application under development.

The interpreter will run all the tables until the end of the document. In this case, the second and third tables compose the business flow example.

The second table indicates to perform the action of opening a checking account 12345-67890 under the name of Spongebob Squarepants on the system under development. That action will result in a call to a method from the Bank fixture. The method will be found using Regular Expression assigned to an annotation.

Annotation & Regular Expression

To resolve the method to call, the ScenarioInterpreter will look at all fixture methods annotated with the following annotations and will try to match the action content to the regular expression on it. Parameters will be captured by the regular expression itself (using capture group).

 @Given

@Given("[regular expression]")

A Given annotation will be use when you need to put the system in a know state before a user interact with it. A good example will be to prepare the data in the system to be available for the next action calls. In our Bank example, this will represent the opening of the checking account action.

@When

@When("[regular expression]")

A When annotation will be use for transiting the system to another state. A good example will be to interact with the system. In our Bank example this will represent deposit or withdraw actions.

@Then

@Then("[regular expression]")

A Then annotation will be use to verify the result of interactions on the system. A good example will be to check if an event has been raised. In our Bank example, this will represent the verification of the balance account.

@Check

@Check("[regular expression]")

A Check annotation will be use to verify the result of the action (boolean result equality). A good example will be to check if we can proceed with an action. In our Bank example, this will represent the verification whenever we can withdraw a certain amount.

@Display

@Display("[regular expression]")

 

A Display annotation will be use to show the result of the action (the actual result). The displayed value is only for information purpose. A good example will be to show an internal value. In our Bank example, this will represent showing the actual balance account.


If we continue with our example, the third table indicates to verify the balance account against the expected value $0.00. Here, we introduce a verification for an expected value and the actual value. This will be done using the Expectation object as a parameter of the method to be call.

The fixture code to support this example in Java is the class BankFixture shown below.

Show me the code

Code for the Bank fixture using annotations for the Scenario Interpreter
public class BankFixture
{
    private Bank bank;

    public BankFixture()
    {
        bank = new Bank();
    }

    @Given("open (\\w+) account (\\d{5}\\-\\d{5}) under the name of ([\\w|\\s]*)")
    public void openAccount(String type, String number, Owner owner)
    {
        if ("checking".equals( type ))
        {
            bank.openCheckingAccount( number, owner );
        }
        else if ("savings".equals( type ))
        {
            bank.openSavingsAccount( number, owner );
        }
    }

    @Then("verify that balance of account (\\d{5}\\-\\d{5}) is (\\$\\d+\\.\\d\\d)")
    public void theBalanceOfAccount(String number, Expectation expectedBalance) throws NoSuchAccountException
    {
        Money actualBalance = bank.getAccount( number ).getBalance();
        expectedBalance.setActual( actualBalance );
    }
}

That class follows the general rules of fixtures described in Fixture Conventions.

The fixture does not much except from delegating the processing to the application code, in this case the Bank class.

Code for the Bank application code
public class Bank {

    private final HashMap<String, BankAccount> accounts;

    public Bank()
    {
        accounts = new HashMap<String, BankAccount>();
    }

    public boolean hasAccount(String accountNumber)
    {
        return accounts.containsKey(accountNumber);
    }

    public BankAccount getAccount(String accountNumber) throws NoSuchAccountException
    {
         if (!hasAccount(accountNumber)
             throw new NoSuchAccountException(accountNumber);
         return accounts.get(accountNumber);
    }

    public CheckingAccount openCheckingAccount(String number, Owner owner)
    {
        if (hasAccount(number)) return null;

        CheckingAccount account = new CheckingAccount(number, owner);
        accounts.put(number, account);
        return account;
    }
}

How is the example interpreted?

When it runs this example,  reads the first table to decide on the interpreter and fixture to use and start testing from the second table, which is the first test table.

The second table is an action which  carries out in the following sequence of steps:

  1. Find a method that match the action content using regular expression defined on annotated method
  2. The parameter Owner will be instanced by the type conversion facility and will contain Spongebob and Squarepants
  3. It calls the method openAccount() with the parameters 12345-67890 and the Owner object instance
  4. The behavior will be applied by the type of the annotation. In this case, it is a Given annotation and the row will not be colored green (except if an exception occur, the row will be colored red).

The third table is an action which  carries out in the following sequence of steps:

  1. Find a method that match the action content using regular expression defined on annotated method
  2. It calls the method theBalanceOfAccount() with the parameter 12345-67890 to get the value calculated by the system under test and the Expectation object instance
  3. This is a Then annotation. The method will set the actual value on the Expectation object instance. LivingDoc will then verify the Expectation object to see if it matches the value $0.00. If the values are equal, it annotates the value $0.00 as right (green) or wrong (red) if not

Building on the Bank example

The second example in Writing a Scenario specification, shown again below, presents a more complete business flow using the bank fixture.

Scenariobank
open checking account 12345-67890 under the name of Spongebob Squarepants
verify that balance of account 12345-67890 is $0.00
deposit $100.00 in account 12345-67890
verify that balance of account 12345-67890 is $100.00
withdraw $50.00 from account 12345-67890
verify that balance of account 12345-67890 is $50.00
can't withdraw $75.00 from account 12345-67890
verify that balance of account 12345-67890 is $50.00
can withdraw $25.00 from account 12345-67890
end

The fourth and last example table contains additional multiple rows. In a sequence of actions, all of the rows in a table are executed, so several actions can be grouped in a table if that helps improve clarity.

Show me the code

Code for the Bank fixture
public class BankFixture
{
    private Bank bank;

    public BankFixture()
    {
        bank = new Bank();
    }

    public BankFixture()
    {
        this.bank = new Bank();
    }

    @Given("open (\\w+) account (\\d{5}\\-\\d{5}) under the name of ([\\w|\\s]*)")
    public void openAccount(String type, String number, Owner owner)
    {
        if ("checking".equals( type ))
        {
            bank.openCheckingAccount( number, owner );
        }
        else if ("savings".equals( type ))
        {
            bank.openSavingsAccount( number, owner );
        }
    }

    @Then("verify that balance of account (\\d{5}\\-\\d{5}) is (\\$\\d+\\.\\d\\d)")
    public void theBalanceOfAccount(String number, Expectation expectedBalance)
            throws NoSuchAccountException
    {
        Money actualBalance = bank.getAccount( number ).getBalance();
        expectedBalance.setActual( actualBalance );
    }

    @When("deposit (\\$\\d+\\.\\d\\d) in account (\\d{5}\\-\\d{5})")
    public void deposit(Money amount, String number)
            throws Exception
    {
        bank.deposit( amount, number );
    }

    @When("withdraw (\\$\\d+\\.\\d\\d) from account (\\d{5}\\-\\d{5})")
    public void withdraw(Money amount, String number)
            throws Exception
    {
        bank.withdraw( amount, number, WithdrawType.ATM );
    }

    @Check("can't withdraw (\\$\\d+\\.\\d\\d) from account (\\d{5}\\-\\d{5})")
    public boolean cannotWithdraw(Money amount, String number)
    {
        try
        {
            bank.withdraw( amount, number, WithdrawType.ATM );
            return false;
        }
        catch (Exception e)
        {
            return true;
        }
    }

    @Check("can withdraw (\\$\\d+\\.\\d\\d) from account (\\d{5}\\-\\d{5})")
    public boolean canWithdraw(Money amount, String number)
    {
        try
        {
            bank.withdraw( amount, number, WithdrawType.ATM );
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

    public Collection<BankAccount> getOpenedAccounts()
    {
        return bank.getAccounts();
    }
}

Combining with other types of rules

An interesting characteristic of the ScenarioInterpreter is the ability to delegate processing of part of the table to another interpreter.

If an interpreter is specified in the first cell of a row, the remainder of the table will be processed by that interpreter. The action for the row must return a fixture that will be used to interpret the rest of the table.

In the example shown below, the first row of the second table indicates to process the rest of the table using a SetOfInterpreter on the value returned by the action opened accounts.

Scenariobank
open checking account 12345-67890 under the name of Spongebob Squarepants
open savings account 54321-09876 under the name of Patrick Star
set ofopened accounts
numbertypeowner name
12345-67890checkingSpongebob Squarepants
54321-09876savingsPatrick Star
end