Skip to end of metadata
Go to start of metadata

You are viewing an old version of this content. View the current version.

Compare with Current View Version History

« Previous Version 5 Next »

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.

simple fixture name
rule forFixtureName
....
list ofFixtureName
....
do withFixtureName
....

 

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.

By using an import table we can remove the package from the fixture name thus improving the readability of the example.

an import table
import
x.y.z

When GreenPepper will search for fixtures in the code, it will look into all the packages specified in the import tables.

Example of fixture name with implicit import
import
com.xyz.mystuff
list ofFixtureName
....

GreenPepper will match com.xyz.stuff.FixtureName.

You can have more than one package or namespace imported in a document. Just add more lines to the import table.

an import table with multiple imports
import
com.xyz.stuff
com.xyz.otherstuff
com.xyz.yapackage

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.

Use a free form with space separating each word for the fixture name and make the fixture class use camel casing.

GreenPepper will match the words of the fixture name with the camel cased class name.

Camel casing multiple word fixture name

fixture name
the fixture                                                                                    TheFixture
a fixture with a very long name becomes                                    AFixtureWithAVeryLongName

When using the humanized version of fixture naming, you must use Developer Guide (Under Construction).

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.

When writing the example, you can omit the suffix Fixture from the fixture name. This keeps the example closer to the real domain.

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

and

rule forbank account
......

will both match

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.

When no parameters are specified in the example, the fixture class must have a public constructor without parameters.

Empty constructor example
rule forbank account
......
public class BankAccountFixture {
   public BankAccountFixture()
   { ... }
}
An example with two parameters
rule forpoker tableante5$max bet

100$

...... 
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, GreenPepper 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 GreenPepper.
  • 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:

Green

 When the expected value matches the returned value, the RuleForInterpreter colors the cell as "right" by coloring it green.

Red

If the values don't match, the RuleForInterpreter colors the cell as "wrong" in red.

YELLOW

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

Grey

If no expected value is specified in a test, the RuleForInterpreter colors the cell in gray and displays the returned value.

Here is an example of cell coloring:

Input Table:

rule forcalculator 
xysum?product()quotient?
1001000
639-5 

Output Table:

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.

A fixture for a table of rules defines how the specific given and expected columns of a rule table are mapped to the system under development.

This page shows the fixture code that supports the examples introduced in the Writing a Rule For specification(link to-do) documentation.

Fixture for Calculator

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

rule forDivision 
dividenddivisorquotient?
6.02.03.0
723.5
183.06
1527

The first cell of the first row indicates that a RuleForInterpreter will be used to interpret the example table. The next cell says that the fixture to use is called Calculator. In Java, the name of the fixture is the name of a Java class.

The second row, also know as the header row, designates the given columns and expected columns. In the example, dividend and divisor are given columns, whereas quotient? is an expected column.

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

Show me the code

Code 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 Fixture Conventions. 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().

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.

For the third row carries out the following sequence of steps:

  1. It assigns the value 6.0 to the dividend instance variable
  2. It assigns the value 2.0 to the divisor instance variable
  3. It calls the method quotient() of the fixture to get the value calculated by the system under development.
  4. It reads the value 3.0 from the quotient? column and compares it to the value returned by the fixture. Since the values are equal, it will annotate the cell as right, which results in the cell being colored green.

Same goes for the fourth and fifth rows.

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.

The Mortgage example described in Writing a Rule For specification document better illustrates the role of the fixture and its interactions with the system under development. This example is shown again here:

rule forinsurance premium fee calculation 
sale pricedown paymentpremium fee?financed amount?
$100,000$15,000$2,125.00 
$100,000$30,000$0.00 
$100,000$25,000$0.00 

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 for the InsurancePremiumFeeCalculation fixture
public class InsurancePremiumFeeCalculationFixture
{
    public Money salePrice;
    public Money downPayment;

    public Money premiumFee()
    {
        InsuranceFee insuranceFee = new InsuranceFee(salePrice);
        return insuranceFee.forDownpayment(downPayment);
    }

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

You might have noticed that the fixture class name is InsurancePremiumFeeCalculationFixture and not InsurancePremiumFeeCalculation. It does not matter to . For fixture class names, the Fixture suffix can be added to make it easier to differentiate from the application under development code. will deal with it automatically.

In this example, the fixture merely delegates the work to the application under development. Namely, the InsuranceFee domain object is responsible for performing the actual work.

The code for the system under development is:

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

This is done automatically for the most common built-in Java types. The mortgage example, however, uses a money type, which is defined by the application under development in the Money business domain class.

When encounters a custom type while performing conversion, it will look for a public static method called parse to handle the conversion from a String to the custom type. To do the conversion from the custom type to a String, it will call the method toString() on the custom type.

When verifies an expected value to an actual value for a custom type, it does the following steps. It:

  1. Converts the expected value specified as a string in the table to the custom type using the parse(String text) method.
  2. Compares the expected value and the actual value using the equals(Object other) method on the expected value, passing it the actual value.

Here's the relevant part of the code for the Money class:

Code 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(column with a ?) is encounterd so you can prepared the return values for this row.

Code with RowAction annotations
 public class DomainStuffFixture
{
    private DomainStuff stuff;
    private int x;
    private int y;

    public void setX(int x) 
    {
         this.x = x;
    } 

    public void setY() 
    {
        this.y = y;
    }

    @BeforeFirstExpectation
    public void allocateDomainStuff() {
        stuff = DomainStuffService.allocateDomainStuff(x, y);
    }

    public int getComputedValueOne() 
    {
         return stuff.getComputedValueOne();
    }

    public int getOtherComputedValueO() 
    {
         return stuff.getOtherComputedValue();
    }

    @AfterRow
    public void freeDomainStuff() {
        DomainStuffService.freeStuff(stuff);
    }

    // ...
}

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.

The second row of the table is the header row and is read from left to right. Two types of columns are supported in rule tables:

  • 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, :

  • figures out the public instance variable to use using camel casing rules.
  • if there is no instance variable that matches the label of the column header, 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.

For an expected 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.

For a given column:

  • It converts the text entered in the cell to the type of the instance variable. To do that it uses either built-in converters (for basic Java types) or delegates to a method called parse (for domain types).
  • It assigns the converted value to the instance variable.
  • However, if type conversion fails, the cell is reported as an error.

For an expected column:

  • 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:

Green

As expected, the row is returned by the system under development.

Red

A row is missing or in surplus in the list returned by the system under development.

YELLOW

If the system encounters an execution error, the cell is in yellow, and provides information about the error.

Particular behavior for the list of interpreters:
Since the ListOfInterpreter expects to match exactly the expected list and the list returned by the SUD, compares each cell of the table separately. If the expected value is different from the returned value, colors the cell in red and provides the two values.

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 definition, the collection interpreters are used to express a collection of data to be compared with the system under development.

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

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

Workflow validation (Do With)

Definition

Writing fixtures for Do With tables

Workflow validation (Scenario)

Context definition (Setup)

Definition

Writing fixtures for Setup tables

Advanced

Defining a custom system under development

Customizing GreenPepper fixture resolution

Hooking document execution

Execute specifications with command line

Examples

Guice Example

Rest fixture

Spring example

Windsor Castle System Under Development

  • No labels

0 Comments

You are not logged in. Any changes you make will be marked as anonymous. You may want to Log In if you already have an account.