Kotlin和Mockito

Kotlin with Mockito

1.简介

Kotlin和Java携手并进。这意味着我们可以在Kotlin项目中利用大量现有的Java库。

在这篇简短的文章中,我们将了解如何在Kotlin中使用Mockito进行模拟。如果您想了解有关该库的更多信息,请查看本文。

2.设定

首先,让我们创建一个Maven项目,并在pom.xml中添加JUnit和Mockito依赖项:

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
    <groupId>org.mockito</groupId>
    mockito-core</artifactId>
    <version>3.3.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

我们还需要告诉Maven,我们正在与Kotlin合作,以便它为我们编译源代码。请查阅Kotlin官方文档,以获取有关如何在pom.xml中进行配置的更多信息。

3.与Kotlin一起使用Mockito

假设我们有一个要测试的实现– LendBookManager。此类依赖于尚未实现的名为BookService的服务:

1
2
3
4
interface BookService {
    fun inStock(bookId: Int): Boolean
    fun lend(bookId: Int, memberId: Int)
}

BookService是在LendBookManager的实例化期间注入的,并且在整个checkout方法中使用了两次,这是我们需要为以下内容编写测试的方法:

1
2
3
4
5
6
7
8
9
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")
        }
    }
}

如果没有模拟BookService的能力,就很难为该方法编写单元测试,这正是Mockito派上用场的地方。

我们仅需两行代码,就可以创建BookService接口的模拟,并在调用inStock()方法时指示其返回固定值:

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

每当使用参数100调用inStock()方法时,这将强制mockBookService实例返回true(请注意,我们必须使用反引号来转义when()方法;这是必需的,因为when是Kotlin语言中的保留关键字)。

然后,我们可以在实例化过程中将此模拟实例传递给LendBookManager,调用我们要测试的方法,并验证该操作是否调用了lend()方法:

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

我们可以快速测试方法实现的其他逻辑路径,如果所需的书没有库存,则应该抛出异常:

1
2
3
4
5
6
7
@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)
}

注意,对于该测试,当询问ID为100的书是否有存货时,我们告诉mockBookService返回false。这将导致checkout()调用引发IllegalStateException。

我们在@Test批注上使用了预期的属性,表明我们希望该测试引发异常。

4. Mockito Kotlin图书馆

我们可以使用一个名为mockito-kotlin的开源库,使代码看起来更像Kotlin。该库在其方法周围包装了Mockito的一些功能,并提供了更简单的API:

1
2
3
4
5
6
7
8
@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)
}

它还提供了其嘲笑()方法的版本。使用此方法时,我们可以利用类型推断,因此可以在不传递任何其他参数的情况下调用该方法。

最终,该库提供了一个新的everyever()方法,该方法可以自由使用,而无需像使用Mockito的本机when()方法时那样必须使用反引号。

查看他们的Wiki以获取增强功能的完整列表。

5.结论

在本快速教程中,我们了解了如何设置项目以将Mockito和Kotlin一起使用,以及如何利用这种组合来创建模拟并编写有效的单元测试。

与往常一样,您可以在GitHub存储库中查看完整的源代码。