Подтвердить что ты не робот

Как создать Spock mocks за пределами класса спецификации?

Мы объединяем тесты Spock с Spring @ContextConfiguration, поэтому мы можем построить beans в контексте Spring, а затем использовать Spock для фактического тестирования. Мы хотели бы добавить spock mocks в наш Spring beans. Для Mockito есть расширение, которое позволяет вам делать такие вещи, как:

 <mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />

а затем ссылайтесь на этот макет на другой Spring beans. Кажется, что нет такого расширения для Спок. С другой стороны, создание этого, вероятно, не слишком много, если вы знаете, как создавать Mocks вне класса Specification. Единственный способ создать макет Spock, о котором я знаю, это:

T Mock(Class<T> type)   

в спецификации. Есть ли какой-нибудь API в Spock для создания Mocks, когда он не находится внутри класса Specification, поэтому я мог бы создавать Spock mocks для контекста Spring?

4b9b3361

Ответ 1

Создание mocks вне класса spec (и использование их в другом классе spec) в настоящее время невозможно. Там есть открытый запрос функции. Это не должно быть слишком сложно реализовать, но для создания spock-core потребуются некоторые изменения. По крайней мере, должен быть способ вручную привязать макет к другому экземпляру экземпляра. Вероятно, было бы также целесообразно переместить API-интерфейс, созданный с помощью пользовательского интерфейса, из базового класса MockingApi.

Вы должны иметь возможность использовать Mockito со Spock, пока вы завершаете весь код проверки, содержащийся в then-блоке, с вызовом вспомогательного метода, который возвращает true (потому что Spock рассмотрит это утверждение). Что-то вроде then: mockito { /* mockito verifications go here */ }.

Ответ 2

Обнаружено простейшее обходное решение для использования макетов Spock в приложении Spring. Здесь моя конфигурация Spring использует макет для basar bean:

@Configuration @Profile("mocking")
class MockingContext {
  @Bean Basar basar(){ new DelegatingBasar() }
}

class DelegatingBasar implements Basar {
  @Delegate Basar delegate
}

И вот простая спецификация Spock, которая создает и использует mock:

@Autowired
Basar basar
Basar basarMock

def setup() {
    basarMock = Mock(Basar)
    basar.delegate = basarMock;
}

def "create a new seller"(User seller) {
    given:
        basarMock.findAllUsers() >> []
    when:
        go "/static/sellers.html"
        waitFor { $("#newUser") }
        $("#newUser").click()
        waitFor { $("#basarNumber") }
        $("#basarNumber").value(seller.basarNumber)
        $("#name").value(seller.name)
        $("#lastname").value(seller.lastname)
        $("#email").value(seller.email)
        $("#saveUser").click()
        waitFor { $("#successfullCreated") }
    then:
        1 * basarMock.saveUser({ newUser ->  
            newUser.basarNumber == seller.basarNumber
            newUser.name        == seller.name
            newUser.lastname    == seller.lastname
            newUser.email       == seller.email
        })
    where:
        seller << [ new User(basarNumber: "100", name: "Christian", lastname: "", email: ""),
                    new User(basarNumber: "ABC", name: "",          lastname: "", email: "")]
}

Ответ 3

Создание mocks вне класса спецификации возможно, поскольку Spock 1.1 с DetachedMockFactory и SpockMockFactoryBean. Поддерживается также пространство имен spock для конфигурации на основе XML. Вы можете найти примеры использования в документации.

Тест Spring с использованием конфигурации на основе Java и DetachedMockFactory выглядит следующим образом:

@ContextConfiguration(classes = [TestConfig, DiceConfig])
class DiceSpec extends Specification {
    @Autowired
    private RandomNumberGenerator randomNumberGenerator

    @Subject
    @Autowired
    private Dice dice

    def "uses the random number generator to generate results"() {
        when:
            dice.roll()

        then:
            1 * randomNumberGenerator.randomInt(6)
    }

    static class TestConfig {
        private final mockFactory = new DetachedMockFactory()

        @Bean
        RandomNumberGenerator randomNumberGenerator() {
            mockFactory.Mock(RandomNumberGenerator)
        }
    }
}

@Configuration
class DiceConfig {
    @Bean
    Dice dice(RandomNumberGenerator randomNumberGenerator) {
        new Dice(randomNumberGenerator)
    }
}

И конфигурация на основе XML будет выглядеть так:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:spock="http://www.spockframework.org/spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.spockframework.org/spring http://www.spockframework.org/spring/spock.xsd">
    <spock:mock id="randomNumberGenerator" class="RandomNumberGenerator"/>
</beans>

Обязательно включите зависимость spock-spring:

testCompile group: 'org.spockframework', name: 'spock-spring', version: '1.1-groovy-2.4-rc-3'

Ответ 4

Это довольно прямолинейно с "pure Spring":

def parentAppCtx = new StaticApplicationContext()
parentAppCtx.beanFactory.registerSingleton("myBean", Mock(MyClass))
parentAppCtx.refresh()
def appCtx = new ClassPathXmlApplicationContext("spring-config.xml", parentAppCtx)

Конечно, предполагается, что вы правильно разложите конфигурацию Spring. (например, если вы переопределяете "myBean" в spring-config.xml, тогда будет использоваться определение в spring-config.xml, так как ApplicationContext по существу является Картой, и самое раннее определение в нем будет побеждено.)