1. Introduction

Kotlin and Java walk hand in hand. This means we can leverage the vast number of existing Java libraries in our Kotlin projects.

In this short article, we’ll see how we can mock using Mockito in Kotlin. If you want to learn more about the library, check out this article.

2. Setup

First of all, let’s create a Maven project and add JUnit and Mockito dependencies in the pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.3.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

We also need to tell Maven that we’re working with Kotlin so that it compiles the source code for us. Check out the official Kotlin documentation for more information on configuring that in the pom.xml.

3. Using Mockito with Kotlin

Suppose we have an implementation we want to test – LendBookManager. This class has a dependency on a service called BookService, which is not yet implemented:

interface BookService {
    fun inStock(bookId: Int): Boolean
    fun lend(bookId: Int, memberId: Int)
}

The BookService is injected during the instantiation of LendBookManager and is used twice throughout the checkout method, which is the method we need to write our test for:

class LendBookManager(val bookService:BookService) {
    fun checkout(bookId: Int, memberId: Int) {
        if(bookService.inStock(bookId)) {
            bookService.lend(bookId, memberId)
        } else {
            throw IllegalStateException("Book is not available")
        }
    }
}

It would be tough to write unit tests for that method without having the ability to mock BookService – which is where Mockito comes in handy.

We can, with just two lines of code, create a mock of the BookService interface and instruct it to return a fixed value when the inStock() method is called:

val mockBookService = Mockito.mock(BookService::class.java)
Mockito.`when`(mockBookService. inStock(100)).thenReturn(true)

This will force the mockBookService instance to return true whenever the inStock() method is called with the argument 100 (notice that we had to escape the when() method using the backtick; this is required since when is a reserved keyword in the Kotlin language).

We can then pass this mocked instance to LendBookManager during instantiation, invoke the method we want to test, and verify that the lend() method was called as a result of the operation:

val manager = LendBookManager(mockBookService)
manager.checkout(100, 1)        
Mockito.verify(mockBookService).lend(100, 1)

We can quickly test the other logical path of our method’s implementation, which should throw an exception if the desired book is not in stock:

@Test(expected = IllegalStateException::class)
fun whenBookIsNotAvailable_thenAnExceptionIsThrown() {
    val mockBookService = Mockito.mock(BookService::class.java)
    Mockito.`when`(mockBookService. inStock(100)).thenReturn(false)
    val manager = LendBookManager(mockBookService)
    manager.checkout(100, 1)
}

Noticed that, for this test, we told mockBookService to return false when asked if the book with id 100 was in stock. This should cause the checkout() invocation to throw an IllegalStateException.

We use the expected property on the @Test annotation, indicating that we expect this test to throw an exception.

4. Mockito Kotlin Library

We can make our code look more Kotlin-like using an open-source library called mockito-kotlin. This library wraps some of Mockito’s functionality around its methods, providing a simpler API:

@Test
fun whenBookIsAvailable_thenLendMethodIsCalled() {
    val mockBookService : BookService = mock()
    whenever(mockBookService.inStock(100)).thenReturn(true)
    val manager = LendBookManager(mockBookService)
    manager.checkout(100, 1)
    verify(mockBookService).lend(100, 1)
}

It also provides its version of the mock() method. When using this method, we can leverage type inference to call the method without passing any additional parameters.

Finally, this library exposes a new whenever() method that can be used freely, without the need for back-ticks like we had to when using Mockito’s native when() method.

Check their wiki for a complete list of enhancements.

5. Conclusion

In this quick tutorial, we looked at how to set up our project to use Mockito and Kotlin together and how we can leverage this combination to create mocks and write effective unit tests.

As always, you can check out the complete source in the GitHub repo.


« 上一篇: Kotlin中的解构声明