【问题标题】:Mock static method with GroovyMock or similar in Spock在 Spock 中使用 GroovyMock 或类似方法模拟静态方法
【发布时间】:2013-04-05 01:09:29
【问题描述】:

第一次来到这里,如果我错过了什么,请道歉。 我希望使用 Spock 绕过对静态方法的调用。反馈会很棒

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

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

public class ClassUnderTest{

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

}

staticMethod 返回一个 ClassWithStatic 的实例 instanceMethod 返回方法其余部分所需的事物

如果我直接执行全局mock,它返回mocked实例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 中继续失败。

【问题讨论】:

标签: unit-testing groovy spock powermock powermockito


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

这是我使用 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 注解是可选的,只有在与现有库有冲突时才使用它

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 您只需将代理类传递给您正在测试的类的构造函数。 这是什么意思?静态方法不是通过实例调用的,因此没有机会将源/代理类传递给测试类。有什么进一步的解释吗?
    • @Lebecca,对,但是如果您创建一个代理类并在构造函数中传递它,那么您可以在 Groovy 中模拟这些包含静态方法的代理类。例如:``` ProxyForStaticMethod proxy = Mock(ProxyForStaticMethod);怪物怪物 = 新怪物(代理); ``` 现在,在编写规范时,您可以通过以下方式“模拟”静态方法:proxy.staticMethod() >> 'mocked static'
    • 这里的问题是要测试的类不是用代理类来构造的,而是用普通类来构造的。我的意思是,为了方便起见,静态类是作为实用程序构建的,包装它对我来说毫无意义。您的方法似乎消除了代码中的直接静态方法调用,对吧?
    • 抱歉回复晚了。通过传入所需的代理类,必须修改被测类以包含用于测试的构造函数。静态方法确实被实际代码调用。但是,在测试文件中,您可以使用此代理阻止实际的静态调用并模拟它以返回任何内容。
    【解决方案4】:

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

    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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 2010-10-13
      • 1970-01-01
      相关资源
      最近更新 更多