Ложный статический метод с GroovyMock или подобным в Spock

Первый таймер, извиняюсь, если что-то пропустил. Я надеюсь обойти вызов статического метода с помощью Спока. Обратная связь была бы отличной

С классными издевательствами я думал, что смогу пройти статический вызов, но не нашел его. Для справки, я нахожусь в процессе модернизации тестов в устаревшей Java. Рефакторинг запрещен. Я использую спок-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 возвращает 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
    }
}

Но если я запускаю methodUnderTest из ClassUnderTest:

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.

5 ответов

Решение

Спок может только высмеивать статические методы, реализованные в Groovy. Для насмешливых статических методов, реализованных в Java, вам нужно использовать такие инструменты, как GroovyMock, PowerMock или JMockit.

PS: Учитывая, что эти инструменты используют некоторые глубокие приемы для достижения своих целей, мне было бы интересно узнать, насколько хорошо они работают вместе с тестами, реализованными в Groovy/Spock (а не в Java/JUnit).

Вот как я решил мою похожую проблему (высмеивая вызов статического метода, который вызывается из другого статического класса) с помощью 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 аннотация является необязательной, используйте ее только в случае возникновения конфликтов с существующими библиотеками

Обходной путь - обернуть вызов статического метода в метод экземпляра.

      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.

Я недавно нашел 'spock.mockfree', это помогает издеваться над конечными классами и статическими классами/методами. Это довольно просто, как и с этой структурой, в этом случае вам нужно будет только Spy() тестируемый класс и @MockStatic статический метод, который вам нужен.

Пример:

Мы использовали статический метод returnA класса StaticMethodClass.

      public class StaticMethodClass {
    public static String returnA() {
        return "A";
    }
}

вот код вызова

      public class CallStaticMethodClass {
    public String useStatic() {
        return StaticMethodClass.returnA();
    }
}

Теперь нам нужно протестировать метод useStatic класса CallStaticMethodClass. Но сам spock не поддерживает фиктивные статические методы, а мы поддерживаем

      class CallStaticMethodClassTest extends Specification {

    def 'call static method is mocked method'() {
        given:
        CallStaticMethodClass callStaticMethodClass = Spy()
        println("useStatic")
        expect:
        callStaticMethodClass.useStatic() == 'M'
    }

    @MockStatic(StaticMethodClass)
    public static String returnA() {
        return "M";
    }
}

Мы используем аннотацию @MockStatic, чтобы отметить, какой класс нужно имитировать. Непосредственно реализовать статический метод, который требует имитирования под ним, сигнатура метода остается прежней, но реализация отличается.

Ссылка на фреймворк:https://github.com/sayweee/spock-mockfree/blob/498e09dc95f841c4061fa8224fcaccfc53904c67/README.md

Я обошел статические методы в Groovy/Spock, создав прокси-классы, которые подставляются в реальный код. Эти прокси-классы просто возвращают статический метод, который вам нужен. Вы просто передаете прокси-классы конструктору класса, который вы тестируете.

Таким образом, когда вы пишете свои тесты, вы обращаетесь к прокси-классу (который затем возвращает статический метод), и вы сможете тестировать таким образом.

Другие вопросы по тегам