JUnit characterization test utility
Java
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
.gitignore
.travis.yml
README.adoc
pom.xml

README.adoc

JUnit Characterization Tests

A simple utility for JUnit 4.6+ (@Rules required) for working with characterization tests

Current status: Build Status

Characterization tests

A characterization test is a means to describe (characterize) the actual behavior of an existing piece of software, and therefore protect existing behavior of legacy code against unintended changes via automated testing.
This term was coined by Michael Feathers

Usage

Having a Legacy Business Object; assuming a hard to test class

package com.example;

public class BusinessClass {

    public String businessMethod(String param) {
        System.out.println("param = " + param);
        final String split = param.split(" ")[0];
        System.out.println("after split = " + split);
        return split;
    }

}

We can introduce a Chracterization Test - to capture the log of the class and later on re-test; to see if the refcatoring impacted the actual output.

public class BusinessClassTest {

    @ClassRule                  (1)
    public static CharacterizationRule rule = aRuleFor(BusinessClassTest.class)
                .build();

    private BusinessClass service = new BusinessClass();

    @Test
    public void just_run_the_method(String parameter) {
        service.businessMethod(parameter);

    }
}
  1. Invoking characterization rule

Note
The junit extension works both with @ClassRule as well as @Rule annotation; at runtime (when org.junit.rules.TestRule.apply() method is invoked) the behaviour gets configured and an output file that gets generated (or consumed for verification) - see the appendToExistingFile configuration flag. Hence running the CharacterizationRule with different annotations changes the behaviour of the whole test. The file is named after the classname (passed as the parameter).

Initially, to create the master characteristics, the test needs running with pinchpoint property. This turns the rule into a recording mode, when a master output is created. All subsequent test runs (without a property) will be checking the output against the master data.

mvn -Dpinchpoint=true -Dtest=BusinessClassTest test

The default output folder is target/test-classes; the file name is the test class cannonical name, followed with a txt extension.

Configuration

JUnit Characterization Rule comes with a handy builder for more extensive configuration. The initial config, as shown in the example is a shorthand and is based on some default values. Whenever this code is used:

CharacterizationRule rule = aRuleFor(BusinessClassTest.class)
            .build();

what happens under the hood is more or less as follows:

String DEFAULT_FOLDER = System.getProperty("java.io.tmpdir");
String DEFAULT_FILENAME = clazz.getCanonicalName() + ".txt";

CharacterizationRule rule = aRuleFor(BusinessClassTest.class)
              .withRules()
                .clearOutputBeforeCapture()      (1)
                .inFolder(DEFAULT_FOLDER)        (2)
                .withFilename(DEFAULT_FILENAME)  (3)
              .up()
            .build();
  1. This directive sets the rule truncate a master file (if exists) so that each log of each rule invocation (each method for @Rule or each test for @ClassRule).

  2. Location of the log file.

  3. Log file name.

I can imagine a situation when you might want to log all output to a single (existing) file in a custom location. Than the configuration might look as follows:

CharacterizationRule rule = aRuleFor(BusinessClassTest.class)
              .withRules()
                .appendToExistingFile()
                .inFolder("/my/custom/location/")
                .withFilename("foo.txt")
              .up()
            .build();

The appending mode comes especially useful when you use multiple parameters for testing (like the @JUnitParams) described in the next section.

JUnit Patams

JUnitParams project adds a new runner to JUnit and provides much easier and readable parametrised tests for JUnit >=4.6.

Tip
Using junitparams is not essential, nonetheless, a reliable characterization tests makes sense when a significant amount of data is pushed through the class in testing

To use JUnitParams, additional dependency is required

<dependency>
    <groupId>pl.pragmatists</groupId>
    <artifactId>JUnitParams</artifactId>
    <version>1.0.2</version>
    <scope>test</scope>
</dependency>

A modified example, with additional junit parameters, might looks as follows

@RunWith(JUnitParamsRunner.class)           (1)
public class BusinessClassTest {

    @ClassRule
    public static CharacterizationRule rule = aRuleFor(BusinessClassTest.class)
                .build();

    private BusinessClass service = new BusinessClass();

    @Test
    @FileParameters("classpath:tst.csv")    (2)
    public void just_run_the_method(String parameter) {
        service.businessMethod(parameter);

    }
}
  1. Runner for parametrized tests

  2. File based paramters - please reffer to JunitParams wiki for more details

Sample CSV file with parameters for this particular example looks as follows

first parameter
second parameter
third parameter

A sample output for this example would be

param = first parameter
after split = first
param = second parameter
after split = second
param = third parameter
after split = third