Working with code R013

R013 - No inputs found that don't throw a trivial exception

eLearning

Overview

If you receive the output code R013, this means that Diffblue Cover cannot write tests for a method because it is unable to find inputs to provide to the method that don't cause trivial exceptions, such as NullPointerException. Please consider the parameters of the method under test, along with the class variables and instance variables it uses to ensure that it is easily possible to assign reasonable values for these quantities that do not cause trivial exceptions to be thrown.

A possible cause of an R013 is that the method under test uses a private member with an unset or bad value, as is shown in the example below:

public class SomeClass() {
  private Runnable value;
  // ...
  public int runSomething() {
    // ...
    value.run();
    // ...
  }
}

There are three solutions to this scenario - ensure that value is initialized with a proper value, make value public, or provide a getter and a setter for value.

Ensure that your method is testable. A good way to do this is to write a test yourself using the available constructor and setter methods.

If you have ensured that the method you want to analyze is testable, and are still getting an R013 code, you can further investigate the cause of the issue.

Find the root cause

A good tool to investigate coverage issues is to get a partial test from Diffblue Cover.

In Cover Plugin for IntelliJ, partial tests are available by default, all you have to do is click on the button to write tests.

In the CLI, pass in the --keep-partial-tests option.

Run/Debug the test

Running the partial test (e.g. with a debugger) can help you find the cause of the issue. Based on what you see, one of the following scenarios may apply.

Missing input values

Consider the following example where we want tests for the setType() method:

package io.diffblue.example;

import java.util.HashMap;

public class SomeClass {

  enum Type {A, B, C}

  private static final HashMap<Integer, Type> TYPE_MAP = new HashMap<>();

  static {
    TYPE_MAP.put(1111, Type.A);
    TYPE_MAP.put(1212, Type.B);
    TYPE_MAP.put(1313, Type.C);
  }

  public String setType(int shiftedTypeId) {
    Type type = TYPE_MAP.get(shiftedTypeId - 1); // Shift the id to confuse Diffblue Cover
    return type.toString();                      // <-- line 19
  }

}

Diffblue Cover will not write a full test and will render an R013 instead. To find out what the issue is, let us get the partial tests as explained earlier. In our case we get one partial test:

  @Test
  @Disabled("TODO: Complete this test")
  void testDiffblueSetType() {
    // TODO: Complete this test.
    //   Reason: R013 No inputs found that don't throw a trivial exception.
    //   Diffblue Cover tried to run the arrange/act section, but the method under
    //   test threw
    //   java.lang.NullPointerException
    //       at io.diffblue.example.SomeClass.setType(SomeClass.java:19)
    //   In order to prevent setType(int)
    //   from throwing NullPointerException, add constructors or factory
    //   methods that make it easier to construct fully initialized objects used in
    //   setType(int).
    //   See https://diff.blue/R013 to resolve this issue.

    // Arrange and Act
    (new SomeClass()).setType(123);
  }

The NullPointerException on line 19 tells us that Diffblue Cover was unable to find a shiftedTypeId that matches one of the keys in the map.

We provide them manually by using custom inputs. In our case, we can create a DiffblueRules.yml file as below and place it at the root of the module:

int:
  - immediate: 1112
    parameter: "shiftedTypeId"

("immediate" stands for constant primitive or String)

This will prompt Diffblue Cover to use 1112 as a value for parameters named shiftedTypeId and thus give us a test that does not throw a NullPointerException:

  @Test
  void testDiffblueSetType() {
    // Arrange, Act and Assert
    assertEquals("A", (new SomeClass()).setType(1112));
  }

Unsettable fields

In this trivial example, there is no way to set name to a non-null value:

package io.diffblue.example;

public class Unsettable {

  private String name;

  public boolean isNameABC() {
    return name.equals("ABC");
  }
}

This can be fixed by providing constructors or setters that assign that field:

  public Unsettable(String name) {
    this.name = name;
  }

  public void setName(String name) {
    this.name = name;
  }

Inputs that are hard to build: mock

If some inputs to the method under test are difficult to build, perhaps because they require complex configuration or need to read some external resources, it might be useful to mock those. Diffblue Cover automatically mocks some objects, but it might be useful to force-mock some specific classes in some cases.

Assuming that BlackBox is difficult to build in the example below:

package io.diffblue.example;

public class BlackBoxSafety {

  public static boolean isSafe(BlackBox blackBox) {
    if (blackBox.getSafetyLevel().equals("SAFE")) {
      return true;
    }
    return false;
  }
}

the CLI provides an option to force-mock a class: --mock=io.diffblue.example.BlackBox which allows Diffblue Cover to produce full coverage without having to build BlackBox:

  @Test
  void testIsSafe() {
    // Arrange
    BlackBox blackBox = mock(BlackBox.class);
    when(blackBox.getSafetyLevel()).thenReturn("Safety Level");

    // Act and Assert
    assertFalse(BlackBoxSafety.isSafe(blackBox));
    verify(blackBox).getSafetyLevel();
  }

  @Test
  void testIsSafe2() {
    // Arrange
    BlackBox blackBox = mock(BlackBox.class);
    when(blackBox.getSafetyLevel()).thenReturn("SAFE");

    // Act and Assert
    assertTrue(BlackBoxSafety.isSafe(blackBox));
    verify(blackBox).getSafetyLevel();
  }

Inputs that are hard to build: refactor

For better results, an alternative to mocking is to refactor the method under test so that the logic can be tested independently of the problematic input.

Take the following example of a method that extracts comments from a file:

  public static List<String> commentLines(File f) throws IOException {
    List<String> commentLines = new ArrayList<>();
    try (BufferedReader br = new BufferedReader(new FileReader(f))) {
      String line;
      while ((line = br.readLine()) != null) {
        if (line.startsWith("#")) {
          commentLines.add(line);
        }
      }
    }
    return commentLines;
  }

Since Diffblue Cover cannot create file resources, no complete test can be produced, but by separating the logic between the file reading and the filtering as follows:

  public static List<String> commentLinesRefactored(List<String> strings) {
    return strings.stream().filter(line -> line.startsWith("#")).collect(Collectors.toList());
  }

  public static List<String> commentLinesRefactored(File f) throws IOException {
    List<String> lines = new ArrayList<>();
    try (BufferedReader br = new BufferedReader(new FileReader(f))) {
      String line;
      while ((line = br.readLine()) != null) {
        lines.add(line);
      }
    }
    return commentLinesRefactored(lines);
  }

we can get unit tests for the method that does not rely on a file resource.

Note that it is also possible to access a property file as a resource using custom inputs.

Dependency on environment variables

This example will yield an R013 because getProperty() will return null unless the system property is set:

package io.diffblue.example;

public class Property {
  public int propertyLength() {
    return System.getProperty("theProperty").length();
  }
}

In Cover CLI, this can be done using the -D option (see Commands & Arguments) e.g. -DtheProperty=something. In Cover Plugin, environment variables can be set in the run configuration.

Last updated