Tutorial - how to write Java unit tests for Kotlin projects

This tutorial provides an example of using Diffblue Cover CLI to write Java unit tests for Kotlin projects. We’ll use the Spring PetClinic Kotlin codebase to demonstrate practical applications of Diffblue Cover in a real-world project.

First clone the Spring PetClinic Kotlin project from GitHub and then compile the project ready for use with Cover CLI. Finally, run the Diffblue Cover preflight checks to make sure everything is ready for writing tests:

$ dcover create --preflight

Now let’s write unit tests for a Kotlin class within the codebase. We’ll focus on a simplified version of a controller class (PetController), responsible for managing pet information:

@Controller
@RequestMapping("/owners/{ownerId}")
class PetController(val pets: PetRepository, val owners: OwnerRepository) {


   private val VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm"


   @PostMapping("/pets/new")
   fun processCreationForm(owner: Owner, @Valid pet: Pet, result: BindingResult, model: Model): String {
       if (StringUtils.hasLength(pet.name) && pet.isNew && owner.getPet(pet.name!!, true) != null) {
           result.rejectValue("name", "duplicate", "already exists")
       }
       owner.addPet(pet)
       return if (result.hasErrors()) {
           model["pet"] = pet
           VIEWS_PETS_CREATE_OR_UPDATE_FORM
       } else {
           this.pets.save(pet)
           "redirect:/owners/{ownerId}"
       }
   }
}

Run the following Cover CLI command to create unit tests for this HTTP POST controller endpoint - we’ll use the fully-qualified class name of the PetController class on the command line to only create tests for that class.

$ dcover create org.springframework.samples.petclinic.owner.PetController

Running this command will trigger Diffblue Cover to create a set of tests for our entire controller class. As a result, we’ll find a new test class PetControllerDiffblueTest within src/test/java:

@ContextConfiguration(classes = {PetController.class})
@ExtendWith(SpringExtension.class)
class PetControllerDiffblueTest {


   @MockBean
   private OwnerRepository ownerRepository;


   @Autowired
   private PetController petController;


   @MockBean
   private PetRepository petRepository;


   @Test
   void testProcessCreationForm() throws Exception {
       // Arrange
       when(petRepository.findPetTypes()).thenReturn(new ArrayList<>());


       Owner owner = new Owner();
       owner.setAddress("42 Main St");
       owner.setCity("Oxford");
       owner.setFirstName("Jane");
       owner.setId(1);
       owner.setLastName("Doe");
       owner.setPets(new HashSet<>());
       owner.setTelephone("6625550144");
       when(ownerRepository.findById(anyInt())).thenReturn(owner);
       MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/owners/{ownerId}/pets/new", 1);


       // Act and Assert
       MockMvcBuilders.standaloneSetup(petController)
           .build()
           .perform(requestBuilder)
           .andExpect(MockMvcResultMatchers.status().isOk())
           .andExpect(MockMvcResultMatchers.model().size(3))
           .andExpect(MockMvcResultMatchers.model().attributeExists("owner", "pet", "types"))
           .andExpect(MockMvcResultMatchers.view().name("pets/createOrUpdatePetForm"))
           .andExpect(MockMvcResultMatchers.forwardedUrl("pets/createOrUpdatePetForm"));
   }


   // more tests for this controller endpoint
}

This auto-created test class contains test methods for all controller endpoints of the PetController class. Cover automatically writes a set of unit tests for each controller endpoint to cover various scenarios. We can immediately run the tests as Diffblue Cover ensures that the tests compile and execute successfully.

As you can see, the test class created by Diffblue Cover is a Java test class. Currently, Diffblue Cover only supports Java test classes as an output format. However, there’s a simple way to automatically convert this test to Kotlin, if needed - see below.

Convert Java unit tests to Kotlin

Converting Java unit tests into Kotlin using IntelliJ’s conversion feature facilitates a smoother transition for companies migrating from Java to Kotlin. This capability not only streamlines the adaptation process by automating the conversion of existing test suites, but also ensures that legacy tests remain functional and relevant in the new Kotlin codebase.

Such a feature is particularly valuable for maintaining test coverage and quality assurance during the migration, allowing teams to leverage Kotlin’s concise syntax and modern features without losing the robustness of their Java-based tests.

To convert Java unit tests into Kotlin in IntelliJ IDEA, open the Java test file in IntelliJ and select Code > Convert Java File to Kotlin File from the menu.

For our auto-created Java test class PetControllerDiffblueTest, we get the following - a quick, simple, and accurate conversion:

 /**
* Method under test:
* [PetController.processCreationForm]
*/
@Test
@Throws(Exception::class)
fun testProcessCreationForm3() {
   // Arrange
   Mockito.`when`(petRepository!!.findPetTypes()).thenReturn(ArrayList())


   val owner = Owner()
   owner.address = "42 Main St"
   owner.city = "Oxford"
   owner.firstName = "Jane"
   owner.id = 1
   owner.lastName = "Doe"
   owner.pets = HashSet()
   owner.telephone = "6625550144"
   Mockito.`when`(ownerRepository!!.findById(ArgumentMatchers.anyInt())).thenReturn(owner)
   val requestBuilder = MockMvcRequestBuilders.post("/owners/{ownerId}/pets/new", 1)
   requestBuilder.contentType("text/plain")


   // Act and Assert
   MockMvcBuilders.standaloneSetup(petController)
           .build()
           .perform(requestBuilder)
           .andExpect(MockMvcResultMatchers.status().isOk())
           .andExpect(MockMvcResultMatchers.model().size(3))
           .andExpect(MockMvcResultMatchers.model().attributeExists("owner", "pet", "types"))
           .andExpect(MockMvcResultMatchers.view().name("pets/createOrUpdatePetForm"))
           .andExpect(MockMvcResultMatchers.forwardedUrl("pets/createOrUpdatePetForm"))
}

Last updated