How to find regressions

This tutorial uses Spring PetClinic with Diffblue Cover Plugin for IntelliJ, to demonstrate finding a regression in refactoring quickly (and thus also avoid merging code containing bugs into the code base).

Please start with the following steps:

  1. git clone https://github.com/Diffblue-benchmarks/Spring-petclinic

  2. cd Spring-petclinic

  3. git checkout a-test-refactoring

  4. Open the project in IntelliJ.

  5. Navigate to the PetTypeFormatter class.

For this tutorial, the parse method in the PetTypeFormatter has an awkward loop that we want to refactor into streams.

@Component
public class PetTypeFormatter implements Formatter<PetType> {
  @Autowired
  private PetRepository pets;
...
  @Override
  public PetType parse(String text, Locale locale) throws ParseException {

     Collection<PetType> findPetTypes = this.pets.findPetTypes();
     for (PetType type : findPetTypes) {
        if (type.getName().equals(text)) {
           return type;
        }
     }
     throw new ParseException("type not found: " + text, 0);
  }
}
@ContextConfiguration(classes = {PetTypeFormatter.class, PetRepository.class})
@ExtendWith(SpringExtension.class)
public class PetTypeFormatterTest {
@MockBean(name = "petRepository")
private PetRepository petRepository;
@Autowired
private PetTypeFormatter petTypeFormatter;

@Test
public void testParse2() throws ParseException {
  PetType petType = new PetType();
  petType.setId(1);
  petType.setName("Dog");
  ArrayList<PetType> petTypeList = new ArrayList<PetType>();
  petTypeList.add(petType);
  when(this.petRepository.findPetTypes()).thenReturn(petTypeList);
  assertSame(petType, this.petTypeFormatter.parse("Dog", new Locale("en")));
}

}

To see how well these tests cover the method, run them With Coverage. The green bars in the margin of the source code show you which lines are covered.

Now, we refactor the method to obtain:

@Component
public class PetTypeFormatter implements Formatter<PetType> {
  @Autowired
  private PetRepository pets;
...
  @Override
  public PetType parse(String text, Locale locale) throws ParseException {

     return this.pets.findPetTypes().stream()
     .filter(type -> type.getId().equals(text))
        .findFirst()
        .orElseThrow(() -> new ParseException("type not found: " + text, 0));
  }
}

When you run the tests now, you’ll see that there are failing tests. That’s because the refactoring contains a bug.

The error message is: type not found: Dog. (That’s even though we use Dog to mock the PetRepository in the failing test and we pass Dog to the parse method.) We’d expect this test to pass. The best thing to do is to put a breakpoint in the failing test and run it With Debugging to step through the implementation and see what is happening.

This reveals that the filter is wrong: we compare the pet type identifier (getId) instead of the pet type name (getName).

Run these tests again for regression testing in order to confirm that the refactoring is now correct and the bug is fixed.

Cover Plugin helps detect unintended changes that we had accidentally introduced.

The diagram below shows our journey through this tutorial:

Last updated