Kotlin中带有实例方法的enum类与它的Java版本非常相似,但是它们的字节码看起来有些不同。 让我们通过使用编写一些测试来了解差异
Spock 。
我们要测试什么?
让我们看一下我们要测试的代码:
enum class EnumWithInstanceMethod {
PLUS {
override fun sign(): String = "+"
},
MINUS {
override fun sign(): String = "-"
};
abstract fun sign(): String
}
显然,可以用更好的方式编写它(例如,使用enum实例变量),但是此示例显示了我们要以最简单的方式进行测试的情况。
如何用Spock进行测试?
最简单的测试(不起作用)
首先,我们可以像使用Java枚举一样编写测试:
def "should use enum method like in java"() {
expect:
EnumWithInstanceMethod.MINUS.sign() == '-'
}
测试失败:
Condition failed with Exception:
EnumWithInstanceMethod.MINUS.sign() == '-'
|
groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS.sign() is applicable for argument types: () values: []
Possible solutions: sign(), sign(), is(java.lang.Object), find(), with(groovy.lang.Closure), find(groovy.lang.Closure)
at com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.should use enum method like in java(EnumWithInstanceMethodTest.groovy:11)
Caused by: groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS.sign() is applicable for argument types: () values: []
Possible solutions: sign(), sign(), is(java.lang.Object), find(), with(groovy.lang.Closure), find(groovy.lang.Closure)
... 1 more
有趣的是……为什么Groovy告诉我们我们正在尝试调用静态方法? 也许我们不使用枚举实例,而是其他东西? 让我们创建一个测试,将枚举实例传递给方法:
static String consume(EnumWithInstanceMethod e) {
return e.sign()
}
def "should pass enum as parameter"() {
expect:
consume(EnumWithInstanceMethod.MINUS) == '-'
}
错误信息:
Condition failed with Exception:
consume(EnumWithInstanceMethod.MINUS) == '-'
|
groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.consume() is applicable for argument types: (java.lang.Class) values: [class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS]
Possible solutions: consume(com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod)
at com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.should pass enum as parameter(EnumWithInstanceMethodTest.groovy:29)
Caused by: groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.consume() is applicable for argument types: (java.lang.Class) values: [class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS]
Possible solutions: consume(com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod)
... 1 more
现在我们看到我们通过了课程
com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS
,而不是枚举实例。
但是它可以在Java中工作...
JUnit中的类似代码可以完美运行,并且测试通过:
@Test
public void shouldReturnSign() {
assertEquals("-", EnumWithInstanceMethod.MINUS.sign());
}
Java可以毫无问题地访问Kotlin的实例方法,因此Groovy可能出了点问题…
但是带有实例方法的Java枚举,例如
public enum EnumWithInstanceMethodInJava {
PLUS {
public String sign() {
return "+";
}
},
MINUS {
public String sign() {
return "-";
}
};
public abstract String sign();
}
在Spock测试中可以正常工作:
def "should use enum method"() {
expect:
EnumWithInstanceMethodInJava.MINUS.sign() == '-'
}
有什么不同?
我们可以通过查看已编译的类来发现差异:
$ tree build/classes/main/
build/classes/main/
└── com
└── github
└── alien11689
└── testingkotlinwithspock
├── AdultValidator.class
├── EnumWithInstanceMethod.class
├── EnumWithInstanceMethodInJava$1.class
├── EnumWithInstanceMethodInJava$2.class
├── EnumWithInstanceMethodInJava.class
├── EnumWithInstanceMethod$MINUS.class
├── EnumWithInstanceMethod$PLUS.class
├── Error.class
├── Ok.class
├── ValidationStatus.class
└── Validator.class
Java生成匿名类(
EnumWithInstanceMethodInJava$1
和
EnumWithInstanceMethodInJava$2
),但Kotlin用这些枚举实例名称( EnumWithInstanceMethod$MINUS
和 EnumWithInstanceMethod$PLUS
)。
它如何与Groovy相关联? Groovy不需要
.class
在访问代码中的类时,因此当我们尝试访问时
EnumWithInstanceMethod.MINUS
,Groovy将其转换为 EnumWithInstanceMethod.MINUS.class
,而不是枚举的实例。 在Java代码中不会发生相同的问题,因为没有 EnumWithInstanceMethodInJava$MINUS
类。
解
知道了差异,我们可以解决在Groovy代码中访问Kotlin的枚举实例的问题。
第一个解决方案是使用以下方法访问枚举实例
valueOf
方法:
def "should use enum method working"() {
expect:
EnumWithInstanceMethod.valueOf('MINUS').sign() == '-'
}
第二种方法是明确告诉Groovy我们要访问作为枚举实例的静态字段:
def "should use enum method"() {
expect:
EnumWithInstanceMethod.@MINUS.sign() == '-'
}
您可以根据代码样式和首选项选择任何一种解决方案。
给我看代码
代码可在此处获得 。
翻译自: https://www.javacodegeeks.com/2018/05/testing-kotlin-spock-enum-instance-method.html