Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Excerpt

Table of Contents

Fixture Conventions

...

Introduction

Even though one of the objectives of is to create a common language between the business experts and the development team, there will always be a certain degree of difference between the natural language and the programing language. Hence comes the reason for having fixtures. Fixtures are the glue between the business expert examples and the software being developed. When running the table, uses a fixture to mediate between the example expressed in the table and the system under test.

Collaboration demands Compromise

The goal of the fixture, is to translate from one language to the other so neither has to compromise their clarity or their design to match the other. The fixture is the compromise. A fixture is any class. It does not have to extend or implement any base class/interface.

The fixture name

The fixture name is found right next to the interpreter specification.

...

Since a fixture is a Java class, when GreenPepper executes the example, it will try to match the fixture name with a class name.

What about packages?

Usually, Java classes are found inside a package and we can explicitly load a class via the package name.

An explicitly imported fixture
rule forcom.xyz.stuff.FixtureName
..

..

Readability

The problem with packages and namespace and with the classes naming convention, camel casing and no spaces, is that they makes the example less readable for the business expert.

To help readability, we have the following options:

Import tables

Import tables are special tables at the beginning of the document.

...

GreenPepper will search for the fixture in each of these packages until it finds a matching class.

Humanized name

Event without the package, programmatic naming conventions are not the most readable form for the name of the example. This can be arranged by following the camel casing conventions.

...

Note

When using the humanized version of fixture naming, you must use 4. Developer Guide Final.

Explicit package won't work ex: |list of | com.xyz.stuff.the fixture name|

The fixture suffix

If you wish to clearly distinguish between your domain classes and the classes that serve as fixture, you can add the suffix Fixture at the end of the fixture classes name.

...

Suffix example
rule forbank account fixture
......

and

rule forbank account
......

will both match

Code Block
languagejava
public class BankAccountFixture{
...
}

Constructor

A fixture can receive parameters during it's construction. You must have a public constructor that matches the numbers of parameters.

...

An example with two parameters
rule forpoker tableante5$max bet

100$

...... 
Code Block
languagejava
public class PokerTableFixture {
   public PokerTableFixture(Ammount ante, Ammount maxBet)
   { ... }
}

Rule validation (Rule For)

Definition

Definition

The RuleForInterpreter is used to express concrete and measurable business rules.
During the execution of the specification, compares the values returned by the system under development against the expected values defined by the Business Expert.

  • The first row of the table indicates the set of rules to be tested by .
  • The next row is called the header row and serves to distinguish the given values and the expected values. Given values serve as inputs to the system, whereas expected values serve as comparison values against values that are actually returned by the system. When a column header ends with special characters ? or (), it denotes an expected value.
  • Finally, the remaining rows capture the examples. They are the executable test rows.

Specific keywords for expected values

offers a list of useful keywords to support the Business Expert.

Empty cellsWhen a test cell is left blank, only shows the returned value
errorWhen you expect an error, specify it in the cell to test that particular behavior

Coloring

will visually show the test result by coloring each testing cell:

...

rule forCalculcator 
xysum?product()quotient?
100100

0


java.lang.ArithmeticException: / by zero
com.greenpepper.fixture.calculator.Calculator.quotient(Calculator.java:63)
639Expected: -5 Received: 182

Writing fixtures for Rule tables

As we've seen in the Rule For Definition, a table of rules is used to express business rules of the application under development.

...

This page shows the fixture code that supports the examples introduced in the Writing a Rule For Specification  documentation.

Fixture for Calculator

Consider the first example of business rule table described in Table of Rules documentation, shown again below.

...

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

Show me the code

Code Block
languagejava
titleCode for the Calculator fixture
public class Division
{
    public double dividend;
    public double divisor;

    public double quotient()
    {
        return dividend / divisor;
    }
}

That class follows the general rules of fixtures described in the 4. Developer Guide Final. It exposes the instance variable dividend to map with the given column dividend. The given column divisor corresponds to the public instance variable divisor. The expected column quotient? is mapped to the public instance method quotient().

Info

This is a very simple example in which the fixture object does not call on the system under development, but performs the calculation. The quotient() method uses the instance variables dividend and divisor to perform the calculation by itself.

How is the table processed?

When it runs this table, reads the first row to select the interpreter and fixture. It then reads the second to know what are the given and expected columns. Finally it starts testing from the third row down to the end of the table.

...

On the other hand, on the last row the comparison will fail. will mark the expected cell wrong. That cell will be colored red and will display a message including the expected value and the actual value.

How are the types of the values handled?

The instance variables dividend and divisor are of type double, so the given values in the example table needs to be double as well. For the fourth row, will automatically convert the given values 7 and 2 to doubles before assigning them to the instance variables.

The return value of the quotient method is also a double, which means that the values provided in the quotient? expected column must be doubles as well. When compares the value returned by the fixture with the value provided as an expectation, it will first convert the expected value to the type of the actual returned value. In the fifth row, it will convert the value 6 to a double and then do the comparison.

A More Realistic Example

The Calculator example is very simple. In a real world example, the fixture code would not perform any real work but would instead delegate to the application under development. In other words, if this was a real application the fixture would not carry out the division operation but call on the system under development. The general rule is to keep the fixture as thin as possible to be merely a mediator between the example table and the application code.

...

This example uses the InsurancePremiumFeeCalculation fixture class. The two given columns, which are sale price and down payment, are mapped respectively to the instance variables salePrice and downPayment. The two expected columns, called premium fee? and financed amount?, correspond respectively to the methods premiumFee() and financedAmount().

How are spaces between words handled?

You have noticed that you can separate words in the example table with spaces. will convert the sequence a space-separated words to a valid Java identifier by removing the spaces and capitalizing the first letter of every word except the first one. This is called camel casing.

  • For class names, uses upper camel casing with the first letter of the identifier capitalized as well. In our example, the label insurance premium fee calculation is converted to the class name InsurancePremiumFeeCalculation.
  • For method or instance variable names, uses lower camel casing where the first letter of the identifier is left in lowercase.

Show me the code

The supporting code is here:

...

Code Block
languagejava
titleCode for the system under development
public class InsuranceFee
{
    private final Money salePrice;

    public InsuranceFee(Money salePrice)
    {
        this.salePrice = salePrice;
    }

    public Money forDownPayment(Money downPayment)
    {
        return downPayment.greaterThan(salePrice.times(Ratio.percent(25))) ? Money.zero() : financedAmount( downPayment ).times( Ratio.of(25, 1000) );
    }

    private Money financedAmount(Money downPayment)
    {
        return salePrice.minus( downPayment );
    }
}

What about the empty column?

The financed amount? expected value is left empty to tell that this is an information column. We don't want to perform any test in that column, but rather show the value to help our understanding of the calculation. The calculated values will be shown when the example is run and marked with the ignored annotation, making them appear gray.

How are application value types handled?

We've seen that will automatically convert the given values in the table to the type of the corresponding instance variable.

...

Code Block
languagejava
titleCode for the Money class
public class Money
{
    private final BigDecimal dollars;

    public Money(BigDecimal dollars)
    {
        this.dollars = dollars;
    }

    public static Money parse(String text)
    {
        return new Money( new BigDecimal( normalize( text ) ) );
    }

    private static String normalize(String text)
    {
        return text.replaceAll( "\\$", "").replaceAll( ",", "").replaceAll( "\\s", "");
    }

    // ...

    public boolean equals(Object other)
    {
        if (other instanceof Money)
        {
            Money that = (Money) other;
            return this.dollars.compareTo( that.dollars ) == 0;
        }
        else
        {
            return false;
        }
    }

    public int hashCode()
    {
        return dollars.hashCode();
    }

    public String toString()
    {
        return "$" + dollars;
    }

    // ...
}

Row actions in ruleFor

There are 3 annotations(attributes) that can be used in the fixture with a RuleForInterpreter.

  • BeforeRow
  • AfterRow
  • BeforeFirstExpectation

BeforeRow

A method annotated with BeforeRow will be call at the beginning of each new row so you can prepare your fixture.

AfterRow

A method annotated with AfterRow will be call at the end of each row so you can reset your fixture.

BeforeFirstExpectation

The method annotated with BeforeFirstExpectation will be called on each row just before the first ExpectedColumn is encountered so you can prepare the return values for this row.

...

The three annotations are optional, you can use none, some or all of them in your fixture depending on your needs.

Wrap Up

When runs a rule table example, it creates a fixture class from the name indicated in the second cell of the first row. That class is used to mediate between the development table and the application code when checking the example against the system under development.

...

  • given values, which are mapped to public instance variables in the fixture class
  • expected values, which are checked against values returned by the corresponding public instance methods

Headers

For a given column header cell, :

...

  • It figures out the public instance method to use using camel casing rules.
  • If there is no such method or if it's not publicly accessible, it marks the cell with an error annotation, causing the cell to appear yellow and to display a stack trace of the error. That column will be ignored entirely.

Columns

will then run all remaining rows one at a time, going through all cells in the row from left to right.

...

  • It converts the text entered in the cell to the return type of the instance method. To do that it uses either built-in converters (for basic Java types) or delegates to a method called parse (for domain types).
  • It compares the actual value to the converted value based on the definition of the equality for the type.
  • If the values match, it marks the cell with a right annotation causing it to appear green.
  • If the values do not match, it marks the cell with a wrong annotation so that it appears red and displays the expected and actual values.
  • However, if type conversion fails or if the method throws an exception the cell is reported as an error.

List Validation (List of, Set of, Superset of, Subset of)

Definition

DEFINITION

The collection interpreters are used to express any kind of groups, lists or sets of values.

When a collection interpreter is executed, compares the list of values expected with the list of values returned by the system under development. The test result depends on the specific collection interpreter selected.

  • As for all other interpreters, the first row of the collection interpreters specifies the name of the interpreter and the name of the collection to be tested.
  • The next row is used to define the name of each attribute related to the members of the collection of data.
  • The following rows are used to specify the set of values to test.

Specific keywords

none

Coloring

will visually indicate the test result by coloring the rows:

...

Particular behavior for the superset of interpreters:
Since the SupersetOfInterpreter accepts that the specification may contain rows that are not contained in the SUD, will not color the rows contained in the specification but not returned by the SUD.

Writing fixtures for List tables

Writing fixtures for List of Value

As we've seen in the Collection Interpreters, the collection interpreters are used to express a collection of data to be compared with the system under development.

...

This page shows the fixture code that supports the examples introduced in the definition.

Using the CollectionInterpreter alone

Consider the example of collection of values table described in definition, shown again below.

...

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

The query method

The method called to get the list of data from the fixture is query() in Java.

...

but the method specifies still have to be parameter-less and return a Collection implementation or an array.

Show me the code

Code Block
languagejava
titleCode for the CanadaProvinceCodesFixture
public class CanadaProvinceCodesFixture
{
    public Set<Province> query()
    {
        return Country.canada().provinces();
    }
}
Code Block
languagejava
titleCode of the System Under Development
public class Country
{
    private String name;
    private Set<Province> provinces = new TreeSet<Province>();

    private Country(String name)
    {
        this.name = name;
    }

    public static Country canada()
    {
        Country canada = new Country("CANADA");
        canada.provinces.add(new Province("ALBERTA","AB"));
        canada.provinces.add(new Province("BRITISH COLUMBIA","BC"));
        canada.provinces.add(new Province("MANITOBA","MB"));
        canada.provinces.add(new Province("NEW BRUNSWICK","NB"));
        canada.provinces.add(new Province("NEWFOUNDLAND and LABRADOR","NL"));
        canada.provinces.add(new Province("NOVA SCOTIA","NS"));
        canada.provinces.add(new Province("NUNAVUT","NU"));
        canada.provinces.add(new Province("ONTARIO","ON"));
        canada.provinces.add(new Province("PRINCE EDWARD ISLAND","PE"));
        canada.provinces.add(new Province("QUEBEC","QC"));
        canada.provinces.add(new Province("SASKATCHEWAN","SK"));
        canada.provinces.add(new Province("YUKON ","YT"));
        return canada;
    }

    public Set<Province> provinces()
    {
        return provinces;
    }
}

public class Province implements Comparable
{
    public String name;
    public String code;

    public Province(String name, String code)
    {
        this.name = name;
        this.code = code;
    }

    public int compareTo(Object o)
    {
        return name.compareTo(((Province)o).name);
    }
}

How is the table processed?

When it runs this table, reads the first row to select the interpreter and fixture. It then reads the second to know what are the attribute columns. Finally it starts testing from the third row down to the end of the table.

...

The last row is not part the set of Province returned by the system under development. In that particular case, will insert the keyword missing and color the row in red.

Using a CollectionInterpreter joined with the DoWithInterpreter

In this particular case, there is only one fixture combining the methods of the CollectionInterpreter and the methods of the DoWithInterpreter. So, in the CollectionInterpreter table, the second cell of the first row says which method to call from the DoWithInterpreter fixture.

...

list ofPhone book entries
FirstNameLastName
FredFlintstone
BettyRubble
GreatGazoo
WilmaFlintstone

Show me the code

Code Block
languagejava
titleCode for the PhoneBookFixture
public class PhoneBookFixture
{
    private PhoneBook phoneBook = new PhoneBook();
    public void insertWithNumber(String firstName, String lastName, String number)
    {
        phoneBook.insert(new PhoneBookEntry(firstName, lastName, number));
    }
    public List<PhoneBookEntry> query()
    {
        return phoneBook.entries();
    }
}
Code Block
languagejava
titleCode of the System Under Development
public class PhoneBook
{
    private List<PhoneBookEntry> entries = new ArrayList<PhoneBookEntry>();

    public void insert(PhoneBookEntry entry)
    {
        entries.add(entry);
    }

    public List<PhoneBookEntry> entries()
    {
        return entries;
    }
}
public class PhoneBookEntry
{
    public String firstName;
    public String lastName;
    public String number;

    public PhoneBookEntry(String firstName, String lastName, String number)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.number = number;
    }
}

Writing fixtures for derived List of Value (SetOf, SubsetOf, SupersetOf)

The fixture writing and results annotations are exactly similar in all points.
Of course the behavior of the test will vary see Collection Interpreters definition for detail.

Workflow validation (Do With)

Definition

DEFINITION

The DoWithInterpreter 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.

  • As for all other interpreters, the first row of the DoWithInterpreter specifies the name of the interpreter and the name of the sequence of actions to be tested. What makes the DoWithInterpreter particular is that it only has to be defined once for all the sequences of actions expressed in a page. Obviously, the DoWithInterpreter must be define before any sequence of actions.
  • The following rows are used to express specific actions.
  • The form of each row of a DoWithInterpreter shall respect the following rules:
    • a row shall begin with a part of the action description,
    • each parameter shall be isolated in a cell,
    • each parameter shall be separated by parts of the action description.
  • An action description can be left blank in order to separate two parameters.
  • The DoWithInterpreter provides a minimum of keywords used to define a specific action.
  • The DoWithInterpreter may also be expressed in Bullet List form or Number List form.

Specific Keywords

offers a list of useful keywords to support the Business Expert. Those keywords are placed at the beginning of an action row.

AcceptConfirm that the action as been executed by the system under development.
CheckVerify the specified expected value with the value returned by the system under development
RejectThe action should not be performed by the system under development (expected errors).
DisplayPrint the value returned by the system under development.

Coloring

will visually show the test result by coloring each testing cell:

...

Standard form (without keyword)Only the Action description will be colored.
AcceptOnly the cell containing the keyword Accept will be colored.
CheckThe cell containing the expected value will be colored. In case of a failure, will show the expected and the returned values.
RejectOnly the cell containing the keyword Reject will be colored.
DisplayA new cell at the end of the row will be colored containing the returned value.

Writing fixtures for Do With tables

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

...

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

Fixture for Bank

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

...

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

Show me the code

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

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

    public boolean openCheckingAccountUnderTheNameOf(String number, String firstName, String lastName)
    {
        return bank.openCheckingAccount(number, new Owner(firstName, lastName)) != null;
    }

    public Money thatBalanceOfAccountIs(String accountNumber) throws Exception
    {
        BankAccount account = bank.getAccount(accountNumber);
        return account.getBalance();
    }
}

That class follows the general rules of fixtures described in 4. Developer Guide Final. It provides public instance methods openCheckingAccount and thatBalanceOfAccount to map respectively to the actions open checking account and that balance of account.

...

Code Block
languagejava
titleCode 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.

...

  1. It calls the method thatBalanceOfAccountIs() with the parameter 12345-67890 to get the value calculated by the system under test
  2. This is a check action, so it reads the value $0.00 from the last cell of the row and compares it to the value returned by the fixture. Since the values are equal, it annotates the last cell as right, which results in the cell being colored green.

What happens for other return types?

Default Rows

Depending on the value returned by the system under test, default actions will annotate keyword cells following these rules:

  • If the value is true, it annotates keyword cells right, making them appear green.
  • If the value is false - indicating a failure - it annotates keyword cells wrong, making them appear red.
  • If the action throws an exception, it annotates the first keyword as an exception, making it appear yellow and display a stack trace of the error.
  • If the action returns another value or nothing, it ignores the result.
Check Rows

Depending on the value returned by the system under test, check actions will annotate the row following these rules:

  • If the returned value matches the expected value, it annotates the last cell right, making it appear green.
  • If the action returns nothing or a value that does not match the expected value - indicating a failure - it annotates the last cell wrong, making it appear red.
  • If the action throws an exception, it annotates the first keyword as an exception, making it appear yellow and display a stack trace of the error.

Building on the Bank example

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

...

In the last row of the last table, the accept special keyword is used to indicate that we expect the last call to succeed. Accept is the opposite of reject. That is, the last withdraw should not return false nor throw an exception.

Reject Rows

Depending on the value returned by the system under test, reject actions will annotate the row following these rules:

  • If the action returns false or throws an exception - indicating a success - it annotates the reject keyword right, making it appear green.
  • If the returned value is anything else or nothing - indicating a failure - , it annotates the reject keyword wrong, making it appear red.
Accept Rows

Depending on the value returned by the system under test, accept actions will annotate the row following these rules:

  • If the returned value is anything except false or an exception - indicating a success -, it annotates the accept keyword right, making it appear green.
  • If the action returns false or throws an exception - indicating a failure - it annotates the accept keyword wrong, making it appear red.

Show me the code

The supporting code is here:

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

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

    public boolean openCheckingAccountUnderTheNameOf(String number, String firstName, String lastName)
    {
        return bank.openCheckingAccount(number, new Owner(firstName, lastName)) != null;
    }

    public Money thatBalanceOfAccountIs(String accountNumber) throws Exception
    {
        BankAccount account = bank.getAccount(accountNumber);
        return account.getBalance();
    }

    public void depositInAccount(Money amount, String accountNumber) throws Exception
    {
        bank.deposit(amount, accountNumber);
    }
    
    public boolean withdrawFromAccount(Money amount, String accountNumber) throws Exception
    {
        return withdrawFromAccountUsing( amount, accountNumber, WithdrawType.ATM );
    }

    public boolean withdrawFromAccountUsing(Money amount, String accountNumber, WithdrawType withdrawType) throws Exception
    {
        try
        {
            bank.withdraw(amount, accountNumber, withdrawType);
        }
        catch (Exception e)
        {
            return false;
        }
        return true;
    }

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

Combining with other types of rules

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

...

set ofopened accounts
numbertypeowner name
12345-67890checkingSpongebob Squarepants
54321-09876savingsPatrick Star
end
Interpret Rows

Depending on the value returned by the system under test, interpret actions will annotate the row following these rules:

  • If the returned value is an object, it uses the interpreter specified in the first cell on that object to interpret the remainder of the table. The row is not annotated.
  • If the action returns nothing, it uses the interpreter without any fixture to interpret the remainder of the table. The row is not annotated.
  • If the action throws an exception - indicating a failure - it annotates the first action keyword as exception, making appear yellow and display a stack trace of the error. The remainder of the table is not interpreted.

Workflow validation (Scenario 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:

...

Panel
bgColorlightgrey
titleBGColorlightgrey
titleGrey
borderStyledashed

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.

...

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.

...

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
Panel
bgColorlightgrey
borderStyledashed
@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
Panel
bgColorlightgrey
borderStyledashed
@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
Panel
bgColorlightgrey
borderStyledashed
@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
Panel
bgColorlightgrey
borderStyledashed
@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
Panel
bgColorlightgrey
borderStyledashed
@Display("[regular expression]")

...

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

Show me the code

Code Block
languagejava
titleCode 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 4. Developer Guide Final.

Info

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

Code Block
languagejava
titleCode 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.

...

  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. GreenPepper 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.

...

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 Block
languagejava
titleCode 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.

...

set ofopened accounts
numbertypeowner name
12345-67890checkingSpongebob Squarepants
54321-09876savingsPatrick Star
end

Live example (will there be one?)

See a live example here(NYI).

Context definition (Setup)

Definition

Definition

The SetUpInterpreter is used to simplify the creation of a particular state for the system under development. Once the state is created, we can focus on the business process to test.

When a setup table is executed, enter data in the system under development to create the desired state.

  • The first row of the table indicates the name of the interpreter and the name of the desired state.
  • The second row is called the header row and serves to identify the data to be inserted in the system under development.
  • Finally, the remaining rows captures the data to be inserted.

Coloring

will visually show the test result by coloring each testing cell:

...

Panel
bgColor#f0e68c
titleBGColor#f0e68c
titleYELLOW
borderStyledashed

If the insertion has failed because a specified value generates an error, colors the cell of the data in error yellow and provides information about the error.
If the system encounters an error not related to a specific data, add a yellow cell at the end of the data row and provides information about the error.

Writing fixtures for Setup tables

As we've seen in Setup Definition, a table of rules is used to simplify the creation of a particular state for the system under development. Once the state is created, we can focus on the business process to test.

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

Fixture to create bank customers

Consider the example of setup table described in Writing a Setup specification:

...

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

Show me the code

Code Block
languagejava
titleCode for the creation of a group of customers fixture
public class AGroupOfCustomersFixture 
{
	public AccountType type;
	public String number, firstName, lastName;
	public Money balance;
	public static Bank bank;
	
	public AGroupOfCustomersFixture()
	{
		bank=new Bank();
	}
	
	@EnterRow
	public void setupAccount()
	{
		if(AccountType.SAVINGS == type)
			bank.openSavingsAccount(number, new Owner(firstName, lastName)).deposit(balance);
		
		else if(AccountType.CHECKING == type)
			bank.openCheckingAccount(number, new Owner(firstName, lastName)).deposit(balance);
	}
}

How is the table processed?

When it runs this table, reads the first row to select the interpreter and fixture. It then reads the second to know what are the attribute columns. Finally it starts creation of entries from the third row down to the end of the table.

Advanced

Defining a custom system under development

Why ?

The system under development is a bridge between your Fixtures and the system your testing.
If you want to change the way is finding/instancing your fixtures, or if you need to hook the document execution, then you can define a Custom System Under Development.

...

Using custom types

All the examples in the documents are in strings, but fixtures want to process and return other data types.

...

Lets look at how will match these.

Converters

Converters are class implementing the interface com.greenpepper.converter.TypeConverter in java.

...

The ArrayConverter calls recursively the other converters depending on the component type the array holds.

Adding and removing new type converters

Adding

The com.greenpepper.GreenPepper class provides a method to add your own type converter

...

The converters are always checked in a LIFO manner. If two converters can process a data type the last one that has been registered will be used. That way, you can provide your own converters in place of the standard converters.

Removing

There are two methods for unregistering added converters, In case you want to use two converters for the same data type during testing.

...

These can be called from a specification or within the custom system under development.

Self conversion

Instead of registering a TypeConverter, you can use self converting types.

...

Code Block
languagejava
public static String toString(T value)

Rules of conversion

From example to fixture

1 First will verify if the type is can self convert (i.e. public static T parse(String) or public static T ValueOf(string))
2 If not, look for a registered TypeConverter that can handles the type.
3 An UnsupportedOperationException will be thrown

From fixture return value to String

1 First will verify if the type is can self revert (i.e. public static String toString(T) or public static T ToString(string))
2 If not, look for a registered TypeConverter that can handles the type.
3 Use the toString() or ToString() method on the data itself.

Customizing GreenPepper fixture resolution

Prerequisite

To change fixtures resolutions you need to define a custom system under development.

Changing how Image Modified is finding your Fixtures

This could be useful when you are using for example an IOC or just want to add locations (packages in java) for to resolve your fixtures.

Only for specifying location to resolve fixtures (packages in java)
Code Block
languagejava
public static class CustomSystemUnderDevelopment extends DefaultSystemUnderDevelopment
    {
        public CustomSystemUnderDevelopment( String... params )
        {
           super.addImport("com.mycompany.fixtures");
           super.addImport("com.mycompany.specials.fixtures");
        }
    }

By this custom system under development you tell to look in "com.mycompany.fixtures" and "com.mycompany.specials.fixtures" to resolve fixtures in specifications that your are running.

Hooking document execution

To hook a document execution, you need to define a custom system under development.

...

Code Block
languagejava
public static class CustomSystemUnderDevelopment extends DefaultSystemUnderDevelopment
    {
        public CustomSystemUnderDevelopment( String... params )
        {
           
        }

       public void onStartDocument(Document document)
       {
          //this method is called before GreenPepper execute a document
       }

       public void onEndDocument(Document document)
       {
          //this method is called after GreenPepper has executed the document              
       }
    }

Execute specifications programmatically

uses two classes to execute specifications programmatically. With this you can include GreenPepper-Tests in the Unittests of your system under develpment.

...

Code Block
languagejava
// ...
new SpecificationRunnerExecutor(runner).execute();
SpecificationRunnerExecutor executor = new SpecificationRunnerExecutor(runner).locale(new Locale(""));
// A specification runner does not have a output file.
if(runner instanceof DocumentRunner) 
    executor.outputFile(outputFile);    
executor.execute("ACalculatorSample.html");
// ...

How to run a specification (suite) using the command line?

Requirements

  1. Installed java runtime
  2. Your compiled fixture classes/jar (System under Test)
  3. Your compiled classes/jar (System under Development)
  4. Your specifications files
  5. greenpepper-cli-plugin-x.x.x.jar
  6. A command line tool.

...

Code Block
languagejava
java -cp greenpepper-core-X.X.X-all.jar;path/to/systemundertest/classes;path/to/systemunderdevelopment/classes; com.greenpepper.runner.Main -s /path/to/myspecs /path/to/outputresults

Configuration

GreenPepper Command line Configuration

How to deal with static fixture fields and the programmatically execution?

You will run into trouble if your tests are using static fields, since these are stored together with the class in the used class loader of the SpecificationBuilder. The error occurs if you are going to use the same class loader for different runners (after the first run all static fields are filled). Therefore a new option was implemented to set a custom fixture class loader. If no one is given the default class loader will be used:

Code Block
languagejava
// ...
ClassLoader myFirstRunFixtureClassLoader = ClassUtils.toClassLoaderWithNoParent("path/to/my/fixture.jar");
ClassLoader mySecondRunFixtureClassLoader = ClassUtils.toClassLoaderWithNoParent("path/to/my/fixture.jar");
 
SpecificationRunnerBuilder builder = new SpecificationRunnerBuilder(specification.getRepository().asCmdLineOption())
    .classLoader(joinClassLoader)
    .specificationRunnerClass(runnerClass)
    .sections(sectionsArray)
    .report(reportClass.getName())
    .systemUnderDevelopment(systemUnderTest.fixtureFactoryCmdLineOption())
    .withMonitor(recorderMonitor)
    .withMonitor(loggingMonitor)
    .outputDirectory(outputFile.getParentFile());
         
builder.fixtureClassLoader(myFirstRunFixtureClassLoader).execute();
builder.fixtureClassLoader(mySecondRunFixtureClassLoader).execute();
// ...

How to add a custom specification parser?

Create a DocumentBuilder

The best and fastest solution would be to create a new com.greenpepper.repository.DocumentBuilder. This builder should translate your custom markup (e.g. Markdown) into HTML and in fact return a new Document build by the com.greenpepper.html.HtmlDocumentBuilder. In fact: build a HtmlDocumentBuilder wrapper.

...

Code Block
languagejava
// ... 
public class MarkdownDocumentBuilder implements DocumentBuilder {
    @Override
    public Document build(Reader reader) throws IOException {
        String fileContent = IOUtil.readContent( reader );
        String htmlContent = new MarkdownProcessor().markdown(markup);
        Reader stringReader = new StringReader(htmlContent);
        return HtmlDocumentBuilder.tablesAndLists().build( reader );
    }
}
Alternative

Another option would be to let the Markup-/downDocumentBuilder only convert to HTML, therefore preventing possible confusion with library names (e.g. HTMLDocumentBuilder).

...

Code Block
languagejava
titleWikiMarkupDocumentBuilder
// ...
public class WikimarkupDocumentBuilder {
    StringWriter writer = new StringWriter();
 
    /* MyLyn WikiText HTML-Builder */
    HtmlDocumentBuilder builder = new HtmlDocumentBuilder(writer, true);
    MarkupParser markupParser = new MarkupParser();
 
    public File build(File file) throws IOException {
        Reader reader = new FileReader(file);
        String html = IOUtils.toString(reader);
 
        markupParser.setMarkupLanguage(new ConfluenceLanguage());
        markupParser.setBuilder(builder);
        markupParser.parse(html);
        String htmlcontent = writer.toString();
 
        File converted = File.createTempFile("wikiconverttemp", null);
        Files.write(converted.toPath(), htmlcontent.getBytes());
        writer.close();
        converted.deleteOnExit();
        return converted;
    }
// ...

Extend the existing Repositories

All repositories are defining their supported file types. Therefore we have also to extend the repository implementations which should support our new file type/specification format.

...

Code Block
languagejava
//...
public enum FileTypes {
    HTML("html"), MARKUP("markup"), CONFLUENCE("confluence"), NOTSUPPORTED("nosup");
//...

How to add aliases for an interpreter (e.g. for i18n purposes)

Aliases can be used to translate interpreter names in specifications. They are case sensitive and can contain special characters (like whitespaces) and umlauts. Whitespaces at the beginning and at the end are always removed.

...

In general there are 3 ways to add an alias for an interpreter:

Programmatically (on runtime)

Code Block
languagejava
GreenPepper.aliasInterpreter("Scenario", ScenarioInterpreter.class);
GreenPepper.aliasInterpreter("My szenario", ScenarioInterpreter.class);
GreenPepper.aliasInterpreter("My szenario", "com.greenpepper.interpreter.ScenarioInterpreter");
GreenPepper.aliasInterpreter("My wesome scenario!", "com.greenpepper.interpreter.AClass$AInnerClass");

 

Edit the default aliases.properties file

This aliases.properties property file is located in the jar of greenpepper core. Using the sources this file can be found under src/main/resourecs/aliases.properties.

Provide an aliases.properties file

You can place a file named "aliases.properties" next to your greenpepper jar. By default the internal jar properties file is used as fallback.

...

Note

Aliases from property files will be processed only once during the first run of a specification or if the according class loader of com.greenpepper.GreenPepper is unloaded (garbage collected).

Usage example

You can use the aliases in any table.

...