Versions Compared

Key

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

Table of Contents
maxLevel3
absoluteUrltrue

 

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

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

1.2. Coloring

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

...

rule forCalculcator 
xysum?product()quotient?
100100

0


java.lang.ArithmeticException: / by zero
info.novatec.testit.livingdoc.fixture.calculator.Calculator.quotient(Calculator.java:63)
639Expected: -5 Received: 182

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

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

2.2 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.2 LD Rule Validation (Rule For). 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.

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

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

2.5. 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().

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

2.7. 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 );
    }
}

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

2.9. 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;
    }

    // ...
}

3. Row actions in RuleFor

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

  • BeforeRow
  • AfterRow
  • BeforeFirstExpectation

3.1. BeforeRow

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

3.2. AfterRow

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

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

4. Setup and Teardown methods for RuleFor tables

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

  • BeforeTable
  • AfterTable

4.1. BeforeTable

A method annotated with BeforeTable works like the 4.2 LD Rule Validation (Rule For) annotation but is called before each Table instead of each row.

4.2. AfterTable

A method annotated with AfterTable works like the 4.2 LD Rule Validation (Rule For) annotation but is called after each Table instead of each row.

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

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

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