我不使用 Kotlin,但我以前使用过 PowerMock 来模拟 final 类/方法和静态方法。我想知道是否可能使用 Spock 的 GroovyMock 和全局 GroovySpy 功能,并在使用 Spock 1.1-groovy-2.4 测试 Java 代码时进行测试。在第一个快速而粗略的测试场景中,它似乎可以工作:
Java 测试类:
package de.scrum_master.stackoverflow;
public final class FinalClass {
public static final String finalStaticMethod() {
return "x";
}
public final String finalMethod() {
return "x";
}
}
斯波克测试:
package de.scrum_master.stackoverflow
import spock.lang.Specification
class FinalClassTest extends Specification {
def "use GroovyMock for final method in final class"() {
given:
FinalClass finalClass = GroovyMock() {
finalMethod() >> "mocked"
}
expect:
finalClass.finalMethod() == "mocked"
}
def "use global GroovySpy for final static method in final class"() {
given:
GroovySpy(FinalClass, global: true)
FinalClass.finalStaticMethod() >> "mocked"
expect:
FinalClass.finalStaticMethod() == "mocked"
}
}
对我来说,测试运行时两种特性方法都是绿色的。也许你可以用我的例子试一试,然后再尝试你的Kotlin类 - 不过后者我不能保证。
注意:Spock手册中提到:
当从Java代码调用时,Groovy Mocks将表现得像普通的mocks。
因此,当将Groovy Mocks注入到正在测试的Java类的依赖项中时,可能会感到失望。
更新:好的,我用另一个使用那些花哨的GroovyMocks的Java类进行了测试,正如上面提到的,它不起作用:
使用模拟类作为依赖项的Java类:
package de.scrum_master.stackoverflow;
public class AnotherClass {
public String doSomething(FinalClass finalClass) {
return finalClass.finalMethod();
}
public String doSomethingElse() {
return FinalClass.finalStaticMethod();
}
}
Spock测试:
package de.scrum_master.stackoverflow
import spock.lang.Specification
class AnotherClassTest extends Specification {
def "indirectly use GroovyMock for final method in final class"() {
given:
FinalClass finalClass = GroovyMock() {
finalMethod() >> "mocked"
}
expect:
new AnotherClass().doSomething(finalClass) == "mocked"
}
def "indirectly use global GroovySpy for final static method in final class"() {
given:
GroovySpy(FinalClass, global: true)
FinalClass.finalStaticMethod() >> "mocked"
expect:
new AnotherClass().doSomethingElse() == "mocked"
}
}
不幸的是,这两个测试都失败了,因为当从Java类中使用时,这些方法并没有被存根。例如,您只能使用PowerMock或Mockito。但是,您仍然可以使用所有其他很棒的Spock功能,例如数据表、@Unroll
等等。
更新2: 解决方案
将以下内容添加到您的Maven构建中(如果您使用Gradle,请执行类似操作):
<dependency>
<groupId>de.jodamob.kotlin</groupId>
<artifactId>kotlin-runner-spock</artifactId>
<version>0.3.1</version>
<scope>test</scope>
</dependency>
现在你可以使用来自项目kotlin-testrunner的SpotlinTestRunner
与注释结合使用,例如:
@OpenedClasses([Foo, Bar, Zot])
@OpenedPackages(["de.scrum_master.stackoverflow", "my.other.package"])
当然,这对于静态方法无效(你仍然需要PowerMock),但你的问题是针对封闭Kotlin类中的非静态方法。使用此测试运行程序,您只需模拟它们,因为一个特殊的类加载器会在测试执行之前通过Javassist打开它们:
package de.scrum_master.stackoverflow
import de.jodamob.kotlin.testrunner.OpenedClasses
import de.jodamob.kotlin.testrunner.OpenedPackages
import de.jodamob.kotlin.testrunner.SpotlinTestRunner
import org.junit.runner.RunWith
import spock.lang.Specification
@RunWith(SpotlinTestRunner)
@OpenedClasses(FinalClass)
class AnotherClassSpotlinRunnerTest extends Specification {
def "use SpotlinRunner to stub final method in final class"() {
given:
FinalClass finalClass = Stub() {
finalMethod() >> "mocked"
}
expect:
new AnotherClass().doSomething(finalClass) == "mocked"
}
}
when
等方法。 我遇到的唯一一个同时使用Spock&Mockito但未使用这些方法的示例是由Spock执行模拟操作的,而(如您发现的)它目前不支持模拟“final”类。 如果Spock无法满足您的要求,则最好降低依赖性并坚持使用Mockito。 - Dioxin