web-dev-qa-db-ja.com

SpockでGroovyMockまたは同様のものを使用したモック静的メソッド

ここで初めて、私が何かを逃した場合はお詫び申し上げます。 Spockを使用して静的メソッドの呼び出しを回避したいと思っています。フィードバックは素晴らしいでしょう

グルーヴィーなモックで、静的な呼び出しを乗り越えることができると思いましたが、見つかりませんでした。背景として、私はレガシー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は、メソッドの残りの部分で必要なものを返します

グローバルモックを直接実行すると、モックされたインスタンスが返されます。

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
    }
}

それは、instanceMethodで失敗し続けるClassWithStaticの実際のインスタンスをスローします。

13
alexgibbs

Spockは、Groovyに実装されている静的メソッドのみをモックできます。 Javaで実装された静的メソッドをモックするには、 GroovyMockPowerMock または JMockit のようなツールを使用する必要があります。

PS:これらのツールが目標を達成するためにいくつかの深いトリックを引き出していることを考えると、(Java/JUnitではなく)Groovy/Spockで実装されたテストと連携するかどうかとどれだけうまく機能するかを知りたいと思います。

19

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アノテーションはオプションであり、既存のライブラリとの競合がある場合にのみ使用してください

2
slashron

Groovy/Spockの静的メソッドを回避する方法は、実際のコードで置き換えられるプロキシクラスを作成することです。これらのプロキシクラスは、必要な静的メソッドを返すだけです。テストしているクラスのコンストラクターにプロキシクラスを渡すだけです。

したがって、テストを作成するときは、プロキシクラスにアクセスすると(静的メソッドが返されます)、その方法でテストできるはずです。

0
Zoose