Java Programming Tutorials

Java programming tutorials with many code examples!

JUnit Theories with built-in ParamaterSuppliers

JUnit Theories comes parameter suppliers that can generate values for test parameters based on specified criteria. In this post we present a number of built-in parameter suppliers, ready for reuse!

Built-in Parameter Suppliers are fairly easy to use and become helpful in some situations. To use a parameter supplier we have to do a few things:

  1. Find existing ParameterSupplier or write one.
  2. Create an annotation to mark our parameters. The annotation will be itself annotated with @ParametersSuppliedBy annotation, which will indicate ParameterSupplier to use.
  3. Annotate input parameters with supplier’s annotation.

Below are examples of how to use them.

For more information on JUnit Theories see our post on DataPoints!

JUnit TestedOnSupplier

The simplest JUnit Parameter Supplier is TestedOnSupplier, which already has its own annotation (@TestedOn). The supplier allows to specify a set of numbers that should be passed to a theory as parameter. It can be used for each parameter in that case all combinations of parameters will be tested. Here’s an example:

package com.farenda.junit;

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.experimental.theories.suppliers.TestedOn;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

@RunWith(Theories.class)
public class TheoriesWithTestedOnSupplierTest {

    @Theory
    public void shouldFindMaxNumbers(
            // Parameters will take only specified values:
            @TestedOn(ints = {1, 2}) int x,
            @TestedOn(ints = {3, 4}) int y) {
        // Will go through all combinations of input data.
        System.out.printf("x: %d, y: %d%n", x, y);
        assertEquals(Math.min(x,y), x);
        assertEquals(Math.max(x,y), y);
    }
}

When you run the code it will print the following result:

x: 1, y: 3
x: 1, y: 4
x: 2, y: 3
x: 2, y: 4

As you can see all combination have been tested.

JUnit BooleanSupplier

Let’s create a Petition class that we would like to test:

package com.farenda.junit;

public class Petition {
    private boolean signed;
    private boolean agreed;

    public void sign(boolean agreed) {
        signed = true;
        this.agreed = agreed;
    }

    public boolean isSigned() {
        return signed;
    }

    public boolean isAgreed() {
        return agreed;
    }
}

Now we can create a JUnit Theory that will test its behavior. In the first place we have to create an annotation (Agreement) to mark input parameters, because JUnit doesn’t have one. Note that we’ve marked it with @ParametersSuppliedBy(BooleanSupplier.class) to let JUnit know which supplier should be used:

package com.farenda.junit;

import org.junit.experimental.theories.ParametersSuppliedBy;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.experimental.theories.internal.BooleanSupplier;
import org.junit.runner.RunWith;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

@RunWith(Theories.class)
public class TheoriesWithBooleanParameterSupplierTest {

    // We need it only at run-time:
    @Retention(RetentionPolicy.RUNTIME)
    // Mark that its values will be provided by BooleanSupplier:
    @ParametersSuppliedBy(BooleanSupplier.class)
    public @interface Agreement {
    }

    @Theory
    public void shouldHaveCorrectPetitionState(
            @Agreement boolean agreed) {
        System.out.println("Signing petition with agreement: " + agreed);
        Petition petition = new Petition();
        petition.sign(agreed);
        assertTrue(petition.isSigned());
        assertThat(petition.isAgreed(), equalTo(agreed));
    }
}

Now, when we run the above code, it will produce the following output:

Signing petition with agreement: true
Signing petition with agreement: false

Note that with JUnit Theories we often test two complementary behaviors, that reverse each other results (like plus and minus).

JUnit EnumSupplier

In this case we’re going to go even deeper and extend EnumSupplier to provide an enum class that should be used as data source for parameters:

package com.farenda.junit;

import org.junit.experimental.theories.*;
import org.junit.experimental.theories.internal.EnumSupplier;
import org.junit.runner.RunWith;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import static org.junit.Assert.assertEquals;

@RunWith(Theories.class)
public class TheoriesWithEnumParameterSupplierTest {

    // Sample enum that we want to test:
    enum Language {
        JAVA, GROOVY, CLOJURE, SCALA;

        public int length() {
            return name().length();
        }
    }

    // Let it know which enum to use as data source:
    public static class LanguageSupplier extends EnumSupplier {
        public LanguageSupplier() {
            super(Language.class);
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @ParametersSuppliedBy(LanguageSupplier.class)
    public @interface Lang {
    }

    @Theory
    public void shouldNotHaveShortLanguagesInEnum(@Lang Language l) {
        System.out.println("Checking language: " + l);
        assertEquals(l.length(), l.toString().length());
    }
}

Pretty simple, no? And here’s the result:

Checking language: JAVA
Checking language: GROOVY
Checking language: CLOJURE
Checking language: SCALA

Outro

In the next post we’re going to implement our own ParameterSupplier. Stay tuned!

Share with the World!