实际上,您在这里提出了一个很好的问题。根据 Spock 手册,您似乎可以使用 GroovyMock 和 GroovyStub 来全局替换实例并像您尝试做的那样存根它们的方法,即使我是您,我也会创建全局模拟对象首先在依赖它的对象的构造函数中隐式使用它之前。但无论如何,它并没有像你说的那样按预期工作。
当我在 Spock 手册和 Spock 源代码中搜索有关 GroovyMock 的示例时,我在任何地方都找不到涉及静态方法以外的任何内容的示例。实际上,那里的测试覆盖率很差。通常,如果手册对我没有帮助,我会查看是否可以从测试中推断出如何使用某个功能。在这种情况下,我不得不自己尝试。
我注意到的第一件事是完全违反直觉的事实:在全局 GroovyMock 或 GroovyStub 上调用构造函数时,它返回 null!!! 这是一个真实的警告。在某种程度上,构造函数在这里被视为普通的模拟方法,也返回null。任何官方消息都没有提到这一点,我也认为它应该改为默认返回一个普通的 Spock 模拟(如果该类是可模拟的,即非最终的)。
现在这也是解决方案的关键:您需要存根一个或多个构造函数以返回除null之外的其他内容,例如以前创建的普通实例或 Spock 模拟/存根/间谍。
这是您的源代码稍作改动的版本。我重命名了应用程序类,以免它们的名称中包含Test。所有这些Test 类名让我有点困惑,特别是因为我还将Spock 规范命名为*Test,因为我通常这样做而不是*Spec,因为这样Maven Surefire/Failsafe 可以自动检测它而无需额外配置。
我还向要模拟的类添加了一个静态方法,以便向您展示存根该类的语法。这只是一个免费插件,与您的问题没有直接关系。
我的测试显示了三个变体:
- 使用经典的 Spock 模拟并将其注入到被测对象中
- 使用全局
GroovySpy,它始终基于真实对象(或指示创建对象)。因此,您不需要存根构造函数。
- 使用带有显式存根构造函数的全局
GroovyMock。在我的示例中,它返回一个带有存根方法的常规 Spock 模拟,但它也可以返回一个普通实例。
package de.scrum_master.stackoverflow.q61667088
class Step {
private String name
Step(String name) {
this.name = name
}
String getName() {
return this.name
}
static String staticMethod() {
return "original"
}
}
package de.scrum_master.stackoverflow.q61667088
class Stage {
Step step
Stage(String name) {
this.step = new Step(name)
}
String run() {
return step.getName()
}
}
package de.scrum_master.stackoverflow.q61667088
import spock.lang.Specification
class GlobalMockTest extends Specification {
def "use Spock mock"() {
given:
def step = Mock(Step) {
getName() >> "Joe"
}
def stage = new Stage("John")
stage.step = step
expect:
stage.run() == "Joe"
}
def "use global GroovySpy"() {
given:
GroovySpy(Step, global: true) {
getName() >> "Joe"
}
Step.staticMethod() >> "stubbed"
def stage = new Stage("John")
expect:
Step.staticMethod() == "stubbed"
stage.run() == "Joe"
}
def "use global GroovyMock"() {
given:
GroovyMock(Step, global: true)
new Step(*_) >> Mock(Step) {
getName() >> "Joe"
}
Step.staticMethod() >> "stubbed"
def stage = new Stage("John")
expect:
Step.staticMethod() == "stubbed"
stage.run() == "Joe"
}
}
PS:我认为,您可能阅读了 Spock 手册,但以防万一:如果您的 GroovyMock/Stub/Spy 目标是用 Groovy 以外的语言实现的,例如 Java 或 Kotlin,这将不起作用,因为那时 Groovy* 将表现得像一个普通的 Spock 模拟/存根/间谍。
更新:我刚刚创建了Spock issue #1159,首先是为了让这种行为记录在案并被测试覆盖,但为了让它也改变,如果它不是这样的意图.