kotlin 不可变data class与mock冲突

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32768743/article/details/88360766

kotlin提倡尽量用val,只是在Spring Boot中一使用val就经常出现一些问题
mock数据就是其中的一个
比如这样一段代码

import com.github.jsonzou.jmockdata.JMockData

data class Demo(
        val id: Int,
        val name: String
)

fun main() {
    val mock = JMockData.mock(Demo::class.java)
}

运行就会报错

Exception in thread "main" com.github.jsonzou.jmockdata.MockException: java.lang.InstantiationException: Demo
	at com.github.jsonzou.jmockdata.mocker.BeanMocker.mock(BeanMocker.java:63)
	at com.github.jsonzou.jmockdata.mocker.ClassMocker.mock(ClassMocker.java:38)
	at com.github.jsonzou.jmockdata.mocker.BaseMocker.mock(BaseMocker.java:35)
	at com.github.jsonzou.jmockdata.JMockData.mock(JMockData.java:33)
	at com.github.jsonzou.jmockdata.JMockData.mock(JMockData.java:21)
	at DemoKt.main(demo.kt:9)
	at DemoKt.main(demo.kt)
Caused by: java.lang.InstantiationException: Demo
	at java.lang.Class.newInstance(Class.java:427)
	at com.github.jsonzou.jmockdata.mocker.BeanMocker.mock(BeanMocker.java:35)
	... 6 more
Caused by: java.lang.NoSuchMethodException: Demo.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 7 more

如果加上NoArg编译插件
如下


@NoArg data class Demo(val id: Int, val name: String)

fun main() {
    val mock = JMockData.mock(Demo::class.java)
    println("mock: $mock")
}

输出为
在这里插入图片描述
JMock数据没有写入,因为用了val
如果改为var
在这里插入图片描述
但是,这样似乎违反了不可变的本意
使用mockk倒是可以使用val,没有问题
在这里插入图片描述
只是这个mock结果,不是我希望的
kotlin的class有一个主构造函数,如果能拿到主构造函数的每个参数和每个参数的类型,再交由JMockData mock数据,最后调用主构造函数,完成mock,是不是能达成曲线救国?
基于上述想法,编写下面的代码,完成mock

import com.github.jsonzou.jmockdata.JMockData
import com.sumixer.ys.api.annotation.NoArg
import kotlin.reflect.KParameter
import kotlin.reflect.full.primaryConstructor

@NoArg data class Demo(val id: Int, val name: String)

fun main() {
    val kClass = Demo::class
    // 获取主构造函数
    val primaryConstructor = kClass.primaryConstructor?:throw RuntimeException("无主构造函数:$kClass")
    println(primaryConstructor)
    // 获取主构造函数的参数和类型
    val parameters = primaryConstructor.parameters
    println(parameters)
    parameters.forEach {
        println("${it.name}: ${it.type}")
    }
    // 根据主构造函数的参数和类型mock数据
    val map = HashMap<KParameter, Any>()
    parameters.forEach {
        // 根据全限定类型名获取对应的类的类型

        // 注意:kotlin.xxx 无法获取成功,需要映射到java类型
        val clazz = Class.forName(map2java(it.type.toString()))
        val mock = JMockData.mock(clazz)
        map[it] = mock
    }
    // 调用主构造函数完成mock
    val demo = primaryConstructor.callBy(map)
    println("mock demo: $demo")

}
fun map2java(name: String): String {
    return when(name) {
        "kotlin.Int" -> "java.lang.Integer"
        "kotlin.String" -> "java.lang.String"
        else -> ""
    }
}

上面的代码需要注意的地方

  • kClass.primaryConstructor 返回为可空,难道有class没有主构造函数?
  • Class.forName 没法成功获取kotlin.xxx类型,需要手动映射
  • 速度比较慢(我的主观感受)
    做到这里,离实际使用还是有一些距离的,现在最注意的问题是需要把泛型加上
    接着写
import com.github.jsonzou.jmockdata.JMockData
import com.sumixer.ys.api.annotation.NoArg
import kotlin.reflect.KParameter
import kotlin.reflect.full.primaryConstructor

@NoArg data class Demo(val id: Int, val name: String)

fun main() {
    val demo = kmockdata<Demo>()
    println("mock demo: $demo")

}
inline fun <reified T : Any> kmockdata() :T{
    val kClass = T::class
    // 获取主构造函数
    val primaryConstructor = kClass.primaryConstructor?:throw RuntimeException("无主构造函数:$kClass")
    println(primaryConstructor)
    // 获取主构造函数的参数和类型
    val parameters = primaryConstructor.parameters
    println(parameters)
    parameters.forEach {
        println("${it.name}: ${it.type}")
    }
    // 根据主构造函数的参数和类型mock数据
    val map = HashMap<KParameter, Any>()
    parameters.forEach {
        // 根据全限定类型名获取对应的类的类型

        // 注意:kotlin.xxx 无法获取成功,需要映射到java类型
        val clazz = Class.forName(map2java(it.type.toString()))
        val mock = JMockData.mock(clazz)
        map[it] = mock
    }
    // 调用主构造函数完成mock
    val mock = primaryConstructor.callBy(map)
    return mock
}
fun map2java(name: String): String {
    return when(name) {
        "kotlin.Int" -> "java.lang.Integer"
        "kotlin.String" -> "java.lang.String"
        else -> ""
    }
}

写泛型,我是蒙圈的
得益于IDEA强大的代码提示功能和自动修复功能,完成泛型的部分
难点

  • T怎么获取KClass
    其实没什么,打两个冒号,IDEA就自动提示了
  • 不是Any的子类,没有primaryConstructor属性
    这个也是靠IDEA的自动修复功能完成的

最后,在我的实际项目中使用,发现无法获取集合泛型的类型,如

List<Goods>

看来接下来的重点,还是处理泛型

猜你喜欢

转载自blog.csdn.net/qq_32768743/article/details/88360766