Mocking Annotations

Mocking annotations allow fine grained control over what mocking should be preferred when testing.

Using @InTestsMock

Perhaps you have a method that Diffblue Cover would ordinarily test using an Integer but you'd prefer to see it tested using Mockito.mock(..). In this case you could annotate the method (or class, or package) to recommend mocking Number:

public class ClassUnderTest {
  @InTestsMock(Number.class)
  public static String methodUnderTest(Number number) {
    return String.valueOf(number.intValue());
  }
}

Setting the priority of the annotation

There are various levels of priority that can be specified for the @InTestMock annotation. The enum MockDecision is provided by the Diffblue Cover Annotation library and can be set to the following values for the `decision' attribute. This attribute is optional and will default to MockDecision.RECOMMENDED if unset.

Value
Description

MockDecision.REQUIRED

The class will be mocked at the highest priority, non-mocks may be provided if they provide more coverage.

MockDecision.RECOMMENDED (default)

The class should be mocked at a high priority, non-mocks may be provided if they provide more coverage.

MockDecision.ALLOWED

Diffblue Cover will try to create an instance of the class itself, if it is unable to, Diffblue Cover will fallback to mock it.

MockDecision.FORBIDDEN

Diffblue Cover will not try to mock this class.

For example, if Diffblue Cover normally mocks a particular class, but there is a specific location where it should not do so, you can forbid mocking in that location.

public class ClassUnderTest { 
  @InTestsMock(value = Number.class, decision = MockDecision.FORBIDDEN)
  public static String methodUnderTest(Number number) {
    return String.valueOf(number.intValue());
  }
}

Suggesting values to return

When mocking a class, it can be useful to provide Diffblue Cover with suggested values that each method can return. This can be achieved using the following (optional) attributes:

Attribute name
Description
Example usage

method

The simple name of the method to mock

method = "intValue"

booleanReturnValues

The boolean value (or an array of values) to return

booleanReturnValues = true or booleanReturnValues = {true,false}

byteReturnValues

The byte value (or an array of values) to return

byteReturnValues = (byte) 1 or byteReturnValues = {(byte) 1,(byte) 0}

charReturnValues

The char value (or an array of values) to return

charReturnValues = 'A' or charReturnValues = {'A','B'}

intReturnValues

The int value (or an array of values) to return

intReturnValues = 2 or intReturnValues = {2,3}

shortReturnValues

The short value (or an array of values) to return

shortReturnValues = 3 or shortReturnValues = {3,4}

longReturnValues

The long value (or an array of values) to return

longReturnValues = 4L or longReturnValues = {4L,5L}

floatReturnValues

The float value (or an array of values) to return

floatReturnValues = 5.0f or floatReturnValues = {5.0f,6.0f}

doubleReturnValues

The double value (or an array of values) to return

doubleReturnValues = 6.0d or doubleReturnValues = {6.0d,7.0d}

stringReturnValues

The String value (or an array of values) to return

stringReturnValues = "AAA" or stringReturnValues = {"AAA","BBB"}

returnValueFactory

The name of the static method to call to create the required object. The syntax is: fully qualified class name + method name, separated by a dot

returnValueFactory = "com.example.Factory.makeUser"

throwException

The class of the exception to throw when the method is called. Must be a subclass of Throwable with an accessible constructor.

throwException = IllegalArgumentException.class

throwExceptionFactory

The name of the static method to call to create the exception instance. The syntax is: fully qualified class name + method name, separated by a dot. The method must return a subclass of Throwable

throwExceptionFactory= "com.example.Factory.createCustomException"

For example, to suggest that Diffblue Cover should return a value of 5 from the mocked method Number.intValue() , the following annotation could be used:

public class ClassUnderTest {
  @InTestsMock(value = Number.class, method = "intValue", intReturnValues = "5")
  public static String methodUnderTest(Number number) {
    return String.valueOf(number.intValue());
  }
}

To return an object, use a factory method. Diffblue Cover will call the specified static method and use its result as the mocked return value.

public class ClassUnderTest {
  @InTestsMock(
    value = AccountService.class,
    method = "getAccount",
    returnValueFactory = "com.example.Factory.createTestAccount"
  )
  public static String methodUnderTest(AccountService service) {
    return service.getAccount().getId();
  }
}

Note that values provided for Diffblue Cover to use while mocking will be prioritised, however if Diffblue Cover is unable to create a test, or if it is not able to generate more coverage using the suggested value, the suggestion may not be used.

Suggesting an array of values

Since a mocked method can only return a single value, why specify an array of values? Diffblue Cover will try to use the values in the order they are specified. The value chosen is the value that provides the best test. In most cases, specifying only a single value will be sufficient for generating a test with good coverage. Not all provided values will be used if Diffblue Cover has already generated a test with good coverage.

Using multiple @InTestsMock annotations

You can specify multiple mocked methods by using the annotation more than once:

@InTestsMock(
  value = UserService.class,
  method = "getName",
  stringReturnValues = {"Alice"}
)
@InTestsMock(
  value = UserService.class,
  method = "getAge",
  intReturnValues = {30}
)
@InTestsMock(
  value = AccountService.class,
  method = "getAccount",
  returnValueFactory = "com.example.Factory.createTestAccount"
)
public boolean isValid(UserService user, AccountService account) {
  return user.getName().length() >= 3
      && user.getAge() >= 18
      && account.getAccount().getId().startsWith("test-");
}

Precedence when using multiple annotations

When multiple @InTestsMock annotations are applied to the same method under test there are a few aspects to understand regarding how Diffblue Cover will utilize the annotations:

  • Diffblue Cover will always favor the annotation nearest the method under test. This allows you to define an annotation at the class level, which will affect all methods in the class, and then override specific methods by defining an annotation at the method level.

  • The MockDecision ('decision') is applied to the Class ('value').

    • If the 'decision' is MockDecision.FORBIDDEN then the values of 'method' and '*returnValues' are ignored.

  • The various 'returnValues' and 'throwException' attributes are applied to the combination of 'value' (i.e. Class) and 'method', and aggregate for a Class#Method combination.

Please see the below illustration:

@InTestsMock(
  value = AccountService.class,
  decision = MockDecision.FORBIDDEN,
  method = "getBalance",// This value will be ignored as decision is FORBIDDEN
  intReturnValues = {5} // This value will be ignored as decision is FORBIDDEN
)
@InTestsMock(
  value = UserService.class,
  decision = MockDecision.ALLOWED,
  method = "getName",
  stringReturnValues = {"Alice"}
)
public class ClassUnderTest {

  @InTestsMock(
    value = UserService.class,
    method = "getName",
    stringReturnValues = {"Bob", "Janice"} // In addition to the class level value
  )
  @InTestsMock(
    value = UserService.class,
    decision = MockDecision.REQUIRED, 
    // mocking UserService will be given the highest priority
    method = "getAge",
    intReturnValues = {30}
  )
  @InTestsMock(
    value = AccountService.class,
    // No defined 'decision', AccountService mocking will be RECOMMENDED
    method = "getAccount",
    returnValueFactory = "com.example.Factory.createTestAccount"
  )
  public boolean isValid(UserService user, AccountService account) {
    return user.getName().length() >= 3
        && user.getAge() >= 18
        && account.getAccount().getId().startsWith("test-")
        && account.getBalance() >= 0;
  }
}

In the example above, Diffblue Cover will configure how it generates mock objects when writing tests for ClassUnderTest#isValid in the following way:

  • When generating a UserService object Diffblue Cover will use MockDecision.REQUIRED

    • When mocking method UserService#getName , suggested values "Bob", "Janice" and "Alice" are provided.

    • When mocking method UserService#getAge , suggested value 30 is provided.

  • AccountService will use MockDecision.RECOMMENDED

    • When mocking method AccountService#getBalance , mock will be configured to return Diffblue Cover generated values.

    • When mocking method AccountService#getAccount , suggested object factory com.example.Factory.createTestAccount is provided.

Using @InTestsMockConstruction

Perhaps you have a method that Diffblue Cover is unable to test, and you think it could make more progress using Mockito.mockConstruction(Random.class). In this case, you could annotate the method (or class, or package) to recommend mocking construction of Random:

public class ClassUnderTest {
  @InTestsMockConstruction(Random.class)
  public static int methodUnderTest() {
    return new Random().nextInt();
  }
}

Note that using @InTestsMockConstruction has the same effect as, and can be overridden by, Cover CLI command line option:

dcover create --mock-construction ClassToMockConstruction

Using @InTestsMockStatic

Perhaps you have a method that Diffblue Cover is unable to test, and you think it could make more progress using Mockito.mockStatic(UUID.class). In this case, you could annotate the method (or class, or package) to recommend mocking static methods of UUID:

public class ClassUnderTest {
  @InTestsMockStatic(UUID.class)
  public static Path methodUnderTest() {
    return Paths.get(UUID.randomUUID() + ".zip");
  }
}

Note that using @InTestsMockStatic has the same effect as, and can be overridden by, Cover CLI command line option:

dcover create --mock-static ClassToMockStatic

Last updated

Was this helpful?