使用 MockK 做测试

接下来的测试将会需要用到 mocking 的 library ,在 Android 大家比较常用的是 MockK 和 Mockito 。这篇要介绍的是 MockK,它是一个专门支援 Kotlin 的 mocking library ,来看看它有哪些特点吧!

  • 简单易懂的 API ,有类似 DSL 的写法。
  • 支援多种 Kotlin 写法的 mocking ,像是 object 、 coroutines extension 、 function 等。
  • 很方便可以验证一些 custom 的逻辑或是程序呼叫顺序。

如果没有写测试经验的朋友应该不太了解甚麽是 mocking ,我们可以用一个例子来了解一下。假如我们有个 ApiService ,它对外提供 User 实体给其他人,而 ApiService 又实作了 Service 这个介面。

data class User(val name: String, val id: Long)

interface Service {
    fun getUser(): User
}

class ApiService : Service {
    override fun getUser() = User("John", 1L)
}

有另一个类别 UserRepository 用到了 Service 介面来提供 User

class UserRepository(private val service: Service) {
    fun getUser(): Flow<User> = flow {
				if (user.id == -1) {
						throw new InvalidUserIdException()
				} else {
						emit(service.getUser())
				}
    }
}

这时候,我们要针对 UserRepository 来写测试的话,首先第一个要做的事情就是控制外部来的变数,也就是 Service ,因为他会控制我们 User 实体。如果我想要 UserRepository 丢出 InvalidUserIdException 错误的话,我可以透过一个假的 service 发出一个 id 为 -1 的假 User 来测试程序是不是会判断 id 为 -1 。相反地,我们也可以测试正向的流程,也就是 id 不为 -1 的流程,这个就是 mocking 可以做的事情。

以上面这个例子如果使用 MockK 可以怎麽测?

@Test
fun `Test getting an user info by MockK`() = runBlocking {
		val fakeUser = User("Doe", 2L)

		val mockApiService = mockk<ApiService>(relaxed = true)
		every { mockApiService.getUser() } returns fakeUser

		val userRepository = UserRepository(mockApiService)
		userRepository.getUser().collect {
	    assertEquals(it, fakeUser)
		}
}

做一个 fakeUser 放到假的 ApiService 里面,在 MockK 要做一个假的物件只要用 mockk<ApiService> 就可以了!下一行的 every...return 表示法就是指定当每次 mockApiService.getUser() 被呼叫的时候要回甚麽指定物件给呼叫的人,这边我们指定 fakeUser 。另外,你可能也有发现 mockk<ApiService> 後面有个 relaxed = true 的参数,这个主要的是让 mocking 的物件内的东西被呼叫的时候,有回给他一个基本值或是不作用,这个的好处是,假设今天我们的 ApiService 有 10 个方法,但我们只要操控 getUser 的结果,另外 9 个不管,我们只要对 getUserevery...return 塞值,另外 9 个不用一个个塞值。

另一个测试路线就是测试当 id 是 -1 的时候会不会丢 exception ,可以在测试方法前面写上 @Test(expected = InvalidUserIdException::class) ,意思就是说我们期待这边会丢出 InvalidUserIdException

@Test(expected = InvalidUserIdException::class)
fun `Test getting an user info by MockK`() = runBlocking {
		val fakeUser = User("Doe", -1L)

		val mockApiService = mockk<ApiService>(relaxed = true)
		every { mockApiService.getUser() } returns fakeUser

		val userRepository = UserRepository(mockApiService)
		userRepository.getUser().collect()
}

MockK 在测试 Kotlin 程序码的时候真的很好用,因为他的 API 是支援 Kotlin 的写法,而且还有类似 DSL 的表达方式,让我们在写测试的时候可以很轻松地完成要写的测试。如果你对於 MockK 有兴趣的话,可以参考一下官网文件,他们的文件也是简单易懂,让测试可以简单就上手。


<<:  Day14 NiFi - NiFi Expression Language

>>:  Day 16 AWS云端实作起手式第六弹 串接两大网路流量导流服务Route53和ELB

Day07,搭配gitlab-ci执行image auto build

正文 今天要设定gitlab上的专案,让他们能够在git commit时自动打包成docker im...

[Day27] Esp32 + IFTTT + Google Sheet

1.前言 今天要讲解如标题一样,Google Sheet是Google所开发的试算表,所以我们要用G...

【把玩Azure DevOps】Day19 CI/CD的关键:Azure DevOps Agent

先前介绍Azure DevOps的Pipelines的时候有提到过Azure DevOps Agen...

Day 18. UX/UI 设计流程之五:GUI Design (下)

上篇说明了 GUI 设计里包含的二个项目:Mockup,以及 Style Guideline。这篇我...

Day22-D3 基础图表:长条图

本篇大纲:开放资料下载、绘制长条图 今天的一天一图表,我们来画图表世界中最常见的 长条图 吧!长条...