Skip to content

0.8.x Tutorial

Hiroshi Ukai edited this page Aug 24, 2017 · 1 revision

Defining a test class

The new test runner is called JCUnit8 (com.github.dakusui.jcunit8.runners.junit4.JCUnit8). When you use the runner, a configuration factory for the test class can be specified it with another annotation @ConfigureWith. With it, you can control various features of JCUnit such as negative test generation, test suite strength, etc.

    @RunWith(JCUnit8.class)
    @ConfigureWith(BankAccountExample.BankAccountConfigFactory.class)
    public class BankAccountExample {
        ...

You can also configure a class from which parameters, constraints, and non-constraint conditions are generated using parameterSpace attribute.

Defining a test method

Methods annotated with @Test are considered to be test methods, but unlike conventional JUnit test methods, they can take parameters. But each of those parameters must be annotated with @From, which specifies how actual argument values of the parameter should be supplied.

      @Test
      @Given("overdraftNotHappens")
      public void whenPerformScenario$thenBalanceIsCorrect(
          @From("scenario") List<String> scenario,
          @From("depositAmount") int amountOfDeposit,
          @From("withdrawAmount") int amountOfWithdraw,
          @From("transferAmount") int amountOfTransfer
      ) {
          ...
      }

@From annotations specify a name of method defined from which actual parameter values should be generated. Those methods must be defined in a class specified by @ConfigureWith#parameterSpace.

  • NOTE: Unlike previous versions, you will not need to use @Uses annotations anymore because the factors used in your test method are already declared by parameter definitions.
Associating test cases and test oracles

Expected behaviours of test cases can be different depending on inputs of them. E.g., when a sequence of bank account operations is executed, the expected outcome will be different depending on whether an overdraft happens or not.

In JCUnit8, this can be expressed by using @Given annotation.

A @Given annotation specifies a condition on which this test method should be executed. In the example above, the test method whenPerformScenario$thenBalanceIsCorrect will be invoked when (and only when) a method overdraftNotHappens, defined in the class specified by @ConfigureWith#parameterSpace attribute, returns true.

Currently JCUnit allows you to create composite conditions using three operators, AND, OR, and NOT from simple ones. With them, you should be able to express any boolean forms in theory (because you can transform any boolean form into DNF)

But parentheses cannot be used.

If you want to express ANDed conditions, you can do it by following.

      @Test
      @Given("condition1&&condition2")
      public void aTestMethod(...) {
          ...

To express ORed conditions,

      @Test
      @Given({"condition1", "condition2"})
      public void aTestMethod(...) {
          ...

And to negate a condition, you can do

      @Test
      @Given({"!condition1", "condition2&&!condition3"})
      public void aTestMethod(...) {
          ...

As mentioned already, parentheses are not supported and you cannot write a condition like this.

   @Given("(condition1||!condition2)&&condition3")

You will need to rewrite this to following.

   @Given({"condition1&&condition3", "!condition2&&condition3"})

Defining a test space

The model from which JCUnit generates test cases consists of three elements which are

  • Parameters
  • Constraints
  • Test oracles

Test oracles are defined as methods in a test class and associated with test cases using @Given annotations. In this section it will be discussed how to define parameters and constraints.

Defining a parameter

When you can list actual values of a parameter and it's sufficient, you can (and should) use "Simple" parameter model. Following is an example.

        @ParameterSource
        public Simple.Factory<Integer> depositAmount() {
          return Simple.Factory.of(asList(100, 200, 300, 400, 500, 600, -1));
        }

The annotation @ParameterSource tells JCUnit that it is a method that supplies actual values of parameter depositAmount (method name). @From annotations reference methods defined in a class that implements Config.Factory and is referred to by @ConfiguredWith annotation.

A method annotated with @ParameterSource must return a factory of a parameter class. And the parameter object created by the returned factory should hold actual values to be used in the generated test suite.

When you want to use a simple parameter, it can be done just by doing

          return Simple.Factory.of(asList(100, 200, 300, 400, 500, 600, -1));

The values 100, 200, 300, ..., passed to (Arrays.)asList are values that you can use the parameter depositAmount.

Defining a constraint and a condition

To define a constraint, you can do following.

        @Condition(constraint = true)
        public boolean overdraftNotHappens(
            @From("scenario") List<String> scenario,
            @From("depositAmount") int amountOfDeposit,
            @From("withdrawAmount") int amountOfWithdraw,
            @From("transferAmount") int amountOfTransfer
        ) {
          return calculateBalance(scenario, amountOfDeposit, amountOfWithdraw, amountOfTransfer) >= 0;
        }

When you want to make it a non-constraint-condition, you can omit the attribute constraint or explicitly set the value to false.

        @Condition(constraint = false)
        public boolean overdraftNotHappens(
  • NOTE: Unlike previous versions, you will not need to use @Uses annotations anymore because the factors used in your test method are already declared by parameter definitions.

Clone this wiki locally