18

初来乍到,如有遗漏,敬请见谅。我希望使用 Spock 绕过对静态方法的调用。反馈会很棒

使用 groovy 模拟,我认为我可以通过静态调用但没有找到它。作为背景,我正在改造遗留 Java 中的测试。禁止重构。我正在使用带有 groovy-1.8 的 spock-0.7。

对静态方法的调用与以下形式的实例调用链接在一起:

public class ClassUnderTest{

public void methodUnderTest(Parameter param){
  //everything else commented out
Thing someThing = ClassWithStatic.staticMethodThatReturnsAnInstance().instanceMethod(param);
   }

}

staticMethod 返回 ClassWithStatic 的实例 instanceMethod 返回方法其余部分所需的 Thing

如果我直接执行全局模拟,它会返回模拟的实例 ok:

def exerciseTheStaticMock(){
    given:
    def globalMock = GroovyMock(ClassWithStatic,global: true)
    def instanceMock = Mock(ClassWithStatic)

    when:
    println(ClassWithStatic.staticMethodThatReturnsAnInstance().instanceMethod(testParam))

    then:
    interaction{
        1 * ClassWithStatic.staticMethodThatReturnsAnInstance() >> instanceMock
        1 * instanceMock.instanceMethod(_) >> returnThing
    }
}

但是,如果我从 ClassUnderTest 运行 methodUnderTest:

def failingAttemptToGetPastStatic(){
    given:
    def globalMock = GroovyMock(ClassWithStatic,global: true)
    def instanceMock = Mock(ClassWithStatic)
    ClassUnderTest myClassUnderTest = new ClassUnderTest()

    when:
    myClassUnderTest.methodUnderTest(testParam)

    then:
    interaction{
        1 * ClassWithStatic.staticMethodThatReturnsAnInstance() >> instanceMock
        1 * instanceMock.instanceMethod(_) >> returnThing
    }
}

它抛出一个 ClassWithStatic 的真实实例,该实例在其 instanceMethod 中继续失败。

4

4 回答 4

26

Spock 只能模拟在 Groovy 中实现的静态方法。要模拟在 Java 中实现的静态方法,您需要使用GroovyMockPowerMockJMockit之类的工具。

PS:鉴于这些工具为了实现它们的目标而采用了一些深刻的技巧,我很想知道它们是否以及如何与在 Groovy/Spock(而不是 Java/JUnit)中实现的测试一起工作。

于 2013-04-05T12:28:28.287 回答
4

以下是我使用 Spock (v1.0) 和 PowerMock (v1.6.4) 解决类似问题的方法(模拟从另一个静态类调用的静态方法调用)

import org.junit.Rule
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.rule.PowerMockRule
import spock.lang.Specification
import static org.powermock.api.mockito.PowerMockito.mockStatic
import static org.powermock.api.mockito.PowerMockito.when

@PrepareForTest([YourStaticClass.class])
@PowerMockIgnore(["javax.xml.*", "ch.qos.logback.*", "org.slf4j.*"])
class YourSpockSpec extends Specification {

@Rule
Powermocked powermocked = new Powermocked();

def "something something something something"() {
    mockStatic(YourStaticClass.class)

    when: 'something something'
    def mocked = Mock(YourClass)
    mocked.someMethod(_) >> "return me"

    when(YourStaticClass.someStaticMethod(xyz)).thenReturn(mocked)

    then: 'expect something'
    YourStaticClass.someStaticMethod(xyz).someMethod(abc) == "return me"

   }
}

注解是可选的,仅在@PowerMockIgnore与现有库有冲突时使用

于 2017-02-27T09:47:44.353 回答
0

我在 Groovy/Spock 中处理静态方法的方法是创建在实际代码中替换的代理类。这些代理类只返回您需要的静态方法。您只需将代理类传递给您正在测试的类的构造函数。

因此,当您编写测试时,您将访问代理类(然后将返回静态方法)并且您应该能够以这种方式进行测试。

于 2019-01-29T17:06:42.497 回答
0

一种解决方法是将静态方法调用包装到实例方法中。

class BeingTested {
    public void methodA() {
        ...

        // was:
        // OtherClass.staticMethod();

        // replaced with:
        wrapperMethod();

        ...
    }

    // add a wrapper method for testing purpose
    void wrapperMethod() {
        OtherClass.staticMethod();
    }
}

现在您可以使用 Spy 来模拟静态方法。

class BeingTestedSpec extends Specification {

    @Subject BeingTested object = new BeingTested()

    def "test static method"() {
        given: "a spy into the object"
        def spyObject = Spy(object)

        when: "methodA is called"
        spyObject.methodA()

        then: "the static method wrapper is called"
        1 * spyObject.wrapperMethod() >> {}
    }
}

如果它应该返回一个值,您还可以为包装器方法存根罐头响应。此解决方案仅使用 Spock 内置函数,并与 Java 和 Groovy 类一起使用,而不依赖于 PowerMock 或 GroovyMock。

于 2021-09-17T04:24:50.933 回答