Как создать моки Спока вне класса спецификации?

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

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

а затем ссылайтесь на этот макет на другие весенние бобы. Похоже, для Спока такого расширения нет. Опять же, создание этого, вероятно, не требует больших усилий, если вы знаете, как создавать Mocks вне класса Specification. Единственный способ создать имитацию Спока, о котором я знаю, это:

T Mock(Class<T> type)   

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


person Jan Thomä    schedule 29.05.2013    source источник


Ответы (4)


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

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

person Peter Niederwieser    schedule 29.05.2013
comment
Я сижу в сеансе SpringOne Spock и testMvc, и начинает казаться очевидным, что для смешивания некоторых имитируемых сервисов и некоторых сервисов, настроенных Spring (например, чтобы избежать логики интеграции вне теста), эта функция будет очень полезно. - person Karl the Pagan; 12.09.2013
comment
Возможно, SpecificationMixin - это то, что я искал? - person Karl the Pagan; 12.09.2013
comment
Я не знаком с SpecificationMixin. В разработке находится пул-реквест, который позволяет создавать макеты извне, и, надеюсь, он будет поставляться со следующей версией Spock. - person Peter Niederwieser; 12.09.2013
comment
@PeterNiederwieser - Есть ли ссылка, которой вы могли бы поделиться, чтобы мы могли более внимательно отслеживать прогресс? Это было бы отличной особенностью для магазинов с большим количеством Java-приложений, которые используют много Spring, чтобы быстрее освоить Spock. - person cdeszaq; 05.12.2013

Нашел простой обходной путь для использования ложных объектов Spock в приложении Spring. Вот моя весенняя конфигурация для использования макета для бина базара:

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

class DelegatingBasar implements Basar {
  @Delegate Basar delegate
}

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

@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: "")]
}
person Christian Baranowski    schedule 22.03.2015
comment
Это изящный обходной путь :). Тем временем мы перешли к более легкому подходу. Мы стараемся полностью избегать создания контекстов Spring в наших модульных тестах и ​​просто имитируем все зависимости сами (см. gist.github.com/derkork/45d7fba64b54a41608e1). Это значительно увеличило производительность наших тестов. Мы используем spring только для сложных вещей, таких как тесты DAO, где мы снимаем реальные утверждения с базой данных в памяти. - person Jan Thomä; 25.03.2015

Создание моков вне класса спецификации возможно, начиная со 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'
person Piotr Góralczyk    schedule 25.03.2017

С "чистой весной" это довольно просто:

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 по сути является картой, и самое последнее определение, помещенное в нее, будет иметь преимущество.)

person Jim Moore    schedule 15.09.2015