掘金 后端 ( ) • 2024-06-21 14:10

theme: vuepress highlight: androidstudio

引言

作为一个纯Kotlin后端的Springboot使用者,单测肯定是开发中必不可少的一部分,但是功能强大的主流框架Mockito由于是用于Java语言的,对于kotlin来说并不是很友好 而且一些关键字冲突比如when,缺少函数式操作使得使用起来总感觉怪怪的,所以使用针对kotlin的mockk单测框架是个非常不错的选择。

添加依赖

mockk并没有提供spring生态的版本,大部分人都是用在安卓开发上。但是mockk提供了对于spring环境的使用建议,springmockk,并且附有spring官网的使用用例,虽然是第三方框架,但是spring官网的指南都是收录的所以不用过于担心。Spring Boot and Kotlin

如果实在想折腾不太信任第三方也可以自行去引用mockk官网的依赖去配置尝试接入到spring环境中

引入springmockk依赖

testImplementation("com.ninja-squad:springmockk:4.0.2")

移除springboot-test中包含的mockito避免依赖冲突

testImplementation("org.springframework.boot:spring-boot-starter-test") {
    exclude(module = "mockito-core")
}

编写单例测试

使用mockk可以当Mockito来用,提供了Mockito所有相关标准的api

//UserControllerTest.kt
@SpringBootTest
class UserControllerTest {
    @Autowired
    private lateinit var userInfoController: UserInfoController
    @MockkBean
    private lateinit var userInfoService: UserInfoService

    @Test
    fun `test api`() {
        every { userInfoService.getUserInfo() } returns "hello1"
        try {
            assertThat(userInfoController.test()).isEqualTo("hello")
        }catch (e: MockKException){
            println("返回值不是预期的hello")
        }
        verify { userInfoService.getUserInfo() }
    }
}

//UserInfoController.kt
@RestController
@CrossOrigin
@RequestMapping("user")
class UserInfoController(
    private val service: UserInfoService,
) {
    @GetMapping("test")
    fun test() = service.getUserInfo()
}

//UserInfoService.kt
@Service
class UserInfoService {
    fun getUserInfo() = "test"
    fun getNewStr(str:String) = "test${str}"
}

版本兼容

mockk springboot jdk 4.x 3.x 17+ 3.x 2.4.x、2.5.x、2.6.x 8+ 2.x 2.2.x、2.3.x 8+ 1.x 2.1.x 8+

JDK动态代理类无法获取问题

在github的说明中有提到在高版本jdk中,比如(Java 16+ AFAIK)中mockk无法对jdk代理的相关模块进行访问,会抛出错误比如

java.lang.IllegalAccessException: class io.mockk.impl.InternalPlatform cannot access a member of class java.lang.reflect.Proxy (in module java.base) with modifiers "protected"

请添加test任务中添加jvm参数用于允许访问动态代理的模块内容

//build.gradle.kts
tasks.withType<Test> {
    jvmArgs(
        "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED"
    )
}