Diffblue Cover Tutorial
Diffblue Cover Tutorial
Diffblue Cover
A Tutorial
Peter Schrammel
Outline
• Diffblue Cover: what it is and how it works
• Using Diffblue Cover
• when refactoring code
• Q&A
What is
Diffblue Cover?
IntelliJ
> Preferences
> Plugins
> Marketplace
> Search for “Diffblue Cover”
> Install Community Edition
https://plugins.jetbrains.com/plugin/14946-diffblue-cover
What is Diffblue Cover?
1. git clone
https://github.com/Diffblue-benchmarks/Spring-petclinic
2. cd Spring-petclinic
3. git checkout a-test-intro
@Service 4. Open project in IntellIJ
public class CloudStorageService { 5. Right-click on CloudStorageService > Write tests
@Autowired
private AmazonS3 s3client;
}
What is Diffblue Cover?
1. git clone
https://github.com/Diffblue-benchmarks/Spring-petclinic
2. cd Spring-petclinic
@ExtendWith(SpringExtension.class) 3. git checkout a-test-intro
public class CloudStorageServiceTest { 4. Open project in IntellIJ
@MockBean
private AmazonS3Client amazonS3Client;
5. Right-click on CloudStorageService > Write tests
@Autowired
private CloudStorageService cloudStorageService;
@Test
public void testDownloadFileFromBucket() throws UnsupportedEncodingException {
StringInputStream objectContent = new StringInputStream("Lorem ipsum dolor sit amet.");
S3Object s3Object = new S3Object();
s3Object.setObjectContent(objectContent);
when(this.amazonS3Client
.getObject(or(isA(String.class), isNull()), or(isA(String.class), isNull())))
.thenReturn(s3Object);
assertSame(s3Object, this.cloudStorageService.downloadFileFromBucket("bucket-name", "key"));
}
....
What does it do?
JUnit
Java Java Create tests
source Compile byte unit source
code code tests code
What does it do?
JUnit
Java Java Create tests
source Compile byte unit source
code code tests code
@MockBean
private AmazonS3Client amazonS3Client;
@Test
public void testDownloadFileFromBucket() throws UnsupportedEncodingException {
StringInputStream objectContent = new StringInputStream( "Lorem ipsum dolor sit amet.");
S3Object s3Object = new S3Object();
s3Object.setObjectContent(objectContent); Arrange inputs and mocks
when(this.amazonS3Client
.getObject( or(isA(String. class), isNull()), or(isA(String. class), isNull())))
.thenReturn(s3Object);
S3Object actualS3Object = this.cloudStorageService.downloadFileFromBucket( "bucket-name", "key")
assertSame(s3Object, actualS3Object);
}
Desirable properties:
What is a unit test? •
•
Runs fast (a few ms)
Has no side effects on
other tests
@MockBean
private AmazonS3Client amazonS3Client;
@Test
public void testDownloadFileFromBucket() throws UnsupportedEncodingException {
StringInputStream objectContent = new StringInputStream( "Lorem ipsum dolor sit amet.");
S3Object s3Object = new S3Object();
s3Object.setObjectContent(objectContent); Arrange inputs and mocks
when(this.amazonS3Client
.getObject( or(isA(String. class), isNull()), or(isA(String. class), isNull())))
Act: run method under test
.thenReturn(s3Object);
S3Object actualS3Object = this.cloudStorageService.downloadFileFromBucket( "bucket-name", "key")
assertSame(s3Object, actualS3Object);
}
Desirable properties:
What is a unit test? •
•
Runs fast (a few ms)
Has no side effects on
other tests
@MockBean
private AmazonS3Client amazonS3Client;
@Test
public void testDownloadFileFromBucket() throws UnsupportedEncodingException {
StringInputStream objectContent = new StringInputStream( "Lorem ipsum dolor sit amet.");
S3Object s3Object = new S3Object();
s3Object.setObjectContent(objectContent); Arrange inputs and mocks
when(this.amazonS3Client
.getObject( or(isA(String. class), isNull()), or(isA(String. class), isNull())))
Act: run method under test
.thenReturn(s3Object);
S3Object actualS3Object = this.cloudStorageService.downloadFileFromBucket( "bucket-name", "key")
assertSame(s3Object, actualS3Object); Assert effects
}
Use case:
Refactoring
1. git checkout a-test-refactoring
@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));
}
}
Refactoring introduces
regression.
Refactoring
fail
Fix bug
class OwnerController {
…
/**
* Counts the number of dogs belonging to the given owner.
* @param owner The owner
* @return The number of pets of type "Dog" belonging to the given owner
*/
public long countDogs(Owner owner) {
return owner.getPets().stream()
"Dog"))
.filter(pet -> pet.getName().equals(
.collect(Collectors.counting());
}
}
1. git checkout a-test-new-feature
pet.setId(1);
pet.setName("Bella");
assertEquals(0L, this.ownerController.countDogs(owner));
}
1. git checkout a-test-new-feature
pet.setId(1);
pet.setName("Bella");
assertEquals(1L, this.ownerController.countDogs(owner));
}
● Speed up test writing
● Demonstrate unintended behavior in
New Feature the implementation
unintended
behavior
/**
* @GetMapping("/owners") public String processFindForm(Owner owner, ...)
* Look up the owner in the database by the given last name.
* If a single owner is found, redirect to /owners/{ownerId}.
* If several owners are found, allow selection of an owner in owners/ownersList.
*/
@WebMvcTest(controllers = {OwnerController.class})
@ExtendWith(SpringExtension.class)
public class OwnerControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean(name = "ownerRepository")
private OwnerRepository ownerRepository;
@MockBean(name = "visitRepository")
private VisitRepository visitRepository;
...
1. git checkout a-test-tdd
…
@Test
public void testProcessFindForm_singleOwner() throws Exception {
Owner owner = new Owner();
owner.setLastName("Doe");
owner.setId(1);
owner.setCity("Oxford");
owner.setAddress("42 Main St");
owner.setFirstName("Jane");
owner.setTelephone("4105551212");
when(this.ownerRepository.findByLastName(or(isA(String.class), isNull())))
.thenReturn(Collections.singletonList(owner));
this.mockMvc.perform(get("/owners").param("lastName", "Doe"))
.andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/1"));
}
…
1. git checkout a-test-tdd
@GetMapping("/owners")
public String processFindForm(
Owner owner, BindingResult result, Map<String, Object> model) {
when(this.ownerRepository.findByLastName(or(isA(String.class), isNull())))
.thenReturn(new ArrayList<Owner>());
when(this.ownerRepository.findByLastName(or(isA(String.class), isNull())))
.thenReturn(new ArrayList<Owner>());
this.mockMvc.perform(get("/owners"))
.andExpect(status().isOk());
.andExpect(view().name("owners/findOwners"));
}
1. git checkout a-test-tdd
2. Implement unit tests for OwnerController.processFindForm
TDD 3. Run unit tests (fail)
4. Implement processFindForm
5. Run unit tests (pass)
public class OwnerController { 6. Right-click on processFindForm > Write tests
… 7. Review created tests
8. Fix unintended behavior in created tests
@GetMapping("/owners")
public String processFindForm( 9. Amend implementation of processFindForm
Owner owner, BindingResult result, Map<String, Object> model) {
pass
Amend requirements
& fix unit tests to Fix imple-
unintended match intended mentation
behavior behavior
Getting Support
https://forum.diffblue.com
I’d like to have
this feature.
Cool stuff!
I don’t get tests!