Customizing test setup

By default, Diffblue Cover will write tests that are executed in isolation but it's possible to provide a custom base class with additional logic to run before / after the tests themselves.

Background

A key feature of Diffblue Cover is automatically configuring classes such that the method under test can be successfully executed.

However, sometimes Diffblue Cover is unable to appropriately configure and clean up the environment such that tests can be usefully written and needs guidance from the user in the form of a custom base class which can provide behavior inherited by the test class.

eLearning

Custom Base Class

When writing tests for com.example.SomeClass Diffblue Cover will look for the existence of com.example.SomeClassDiffblueBase and use that as a super-class when writing com.example.SomeClassDiffblueTest. This com.example.SomeClassDiffblueBase class can contain methods to be run before/after writing tests, correctly annotated according to the testing framework used. The base class needs to have the same name as the class under test, with a DiffblueBase suffix appended, it must be in the same package as the class under test, but likely should be defined in the src/test/java source tree so that it’s kept separate from production code.

Note that if you're creating tests with TestNG for a class that uses Spring, the custom base class feature cannot be used.

Given a class under test: src/main/java/com/example/SomeClass.java

package com.example;

class SomeClass {
  void methodUnderTest() {
    // ...
  }
}

and a custom base class: src/test/java/com/example/SomeClassDiffblueBase.java

package com.example;

class SomeClassDiffblueBase {

}

then Cover will write a test class src/test/java/com/example/SomeClassDiffblueTest.java extending that custom base class.

package com.example;

import org.junit.Test;

class SomeClassDiffblueTest extends SomeClassDiffblueBase {
  @Test
  void testMethodUnderTest() {
    // Arrange
    // ...

    // Act
    // ...

    // Assert
    // ...
  }
}

Note: Once the custom base class has been written or updated, please make sure that it has been compiled so that Cover is able to find the compiled class.

@BeforeXXX Annotated Methods

The most common use case for using a custom base class is to customize the test setup, often to perform some static initialization of some environmental state.

For example, your base class might need to switch to some test environment before tests are run at all, and might need to reset some license limits ahead of each test run.

If your class under test needs some shared environment state to be set up before tests are run at all, you can add a static, visible, no-argument method to your base class annotated with org.junit.jupiter.api.BeforeAll (or org.junit.BeforeClass if using JUnit 4, respectively org.testng.annotations.BeforeClass if using TestNG) performing that initialization logic.

If your methods under test need some state setup or reset before each test is run, you can add a non-static, visible, no-argument method to your base class annotated with org.junit.jupiter.api.BeforeEach (or org.junit.Before if using JUnit 4, respectively org.testng.annotations.BeforeMethod if using TestNG) performing that initialization logic.

For example, your base class might look like the following, with setupTestEnvironment() being called once before SomeClassDiffblueTest, and resetLicenseLimits() being called before each test method found:

package com.example;

import org.junit.BeforeClass;
import org.junit.Before;

class SomeClassDiffblueBase {

  @BeforeClass
  public static void setupTestEnvironment() {
    // custom static configuration
    Environment.state = new TestEnvironment();
  }

  @Before
  public void resetLicenseLimits() {
    // reset license limits
    Licensing.remainingUsages = 100;
  }
}

@AfterXXX Annotated Methods

A less common use case for a custom base class is to customize the test cleanup logic.

If your class under test needs some shared environment state to be cleared after tests are all run, you can add a static, visible, no-argument method to your base class annotated with org.junit.jupiter.api.AfterAll (or org.junit.AfterClass if using JUnit 4, respectively org.testng.annotations.AfterClass if using TestNG) performing that logic.

If your method under test needs some state reset after each test is run, you can add a non-static, visible, no-argument method to your base class annotated with org.junit.jupiter.api.AfterEach (or org.junit.After if using JUnit 4, respectively org.testng.annotations.AfterMethod if using TestNG) performing that logic.

For example, your base class might look like the following, with resetLicenseUsageCount() being called after each test in SomeClassDiffblueTest and then resetTestEnvironment() called after all the tests:

package com.example;

import org.junit.AfterClass;
import org.junit.After;

class SomeClassDiffblueBase {

  @AfterClass
  public static void resetTestEnvironment() {
    // clear test configuration
    Environment.state = null;
  }

  @After
  public void resetLicenseUsageCount() {
    // reset license limits
    Licensing.usageCount = 0;
  }
}

Field Inputs

Another use case for custom base classes is to provide initialized inputs that Cover would otherwise struggle to get right.

For example, perhaps your method under test requires some ComplexObject in order to execute, but it requires multiple calls to configure.

Note: Currently only static fields can be used.

Given a class under test: src/main/java/com/example/SomeClass.java

package com.example;

class SomeClass {
  void methodUnderTest(ComplexObject complex) {
    // ...
  }
}

and a custom base class: src/test/java/com/example/SomeClassDiffblueBase.java

package com.example;

import org.junit.Before;

class SomeClassDiffblueBase {

  static ComplexObject complex;

  @Before
  public void resetTestEnvironment() {
    complex = ComplexObject.builder()
      .withName("For Unit Tests")
      .withSquareNumber(64)
      .build();
  }
}

Cover will use that initialized field to write the test class: src/test/java/com/example/SomeClassDiffblueTest.java

package com.example;

import org.junit.Test;

class SomeClassDiffblueTest extends SomeClassDiffblueBase {

  @Test
  public void testMethodUnderTest() {
    // Arrange
    SomeClass someClass = new SomeClass();

    // Act
    someClass.methodUnderTest(SomeClassDiffblueBase.complex);

    // Assert
    // ...
  }
}

Last updated