How to create Spock mocks outside of a specification class?

半腔热情 提交于 2019-11-30 07:25:45

问题


We combine Spock tests with Spring's @ContextConfiguration so we can build beans in a spring context and then use Spock for the actual testing. We would like to inject spock mocks into our spring beans. For Mockito there is an extension which allows you to do things like:

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

and then reference this mock to other spring beans. There seems to be no such extension for Spock. Then again building this is probably not too much effort if you know how to create Mocks outside of the Specification class. The only way of creating a Spock mock that I'm aware of is:

T Mock(Class<T> type)   

in Specification. Is there some API in Spock to create Mocks when not being inside the Specification class, so I could create Spock mocks for a spring context?


回答1:


Creating mocks outside a spec class (and using them in another spec class) isn't currently possible. There's an open feature request for this. It shouldn't be too hard to implement this, but it would require some changes to spock-core. At the very least, there would need to be a way to manually attach a mock object to another spec instance. Probably, it would also make sense to move the user-facing mock creation API out of the MockingApi base class.

You should be able to use Mockito with Spock, as long as you wrap all verification code contained in a then-block with a call to a helper method that returns true (because Spock will consider it an assertion). Something like then: mockito { /* mockito verifications go here */ }.




回答2:


Found a simple workaround to use Spock mock objects in a Spring application. Here my spring configuration to use a mock for the basar bean:

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

class DelegatingBasar implements Basar {
  @Delegate Basar delegate
}

And here a simple Spock specification which creates and use a 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:


Creation of mocks outside of a specification class is possible since Spock 1.1 with DetachedMockFactory and SpockMockFactoryBean. spock namespace for XML-based configuration is supported as well. You can find usage examples in the documentation.

A Spring test using Java-based configuration and DetachedMockFactory looks like this:

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

And XML-based configuration would look like this:

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

Make sure to include the spock-spring dependency:

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



回答4:


This is pretty straight-forward with "pure Spring":

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

Of course that assumes that you're properly decomposing your Spring configuration. (e.g., If you redefine "myBean" in spring-config.xml then the definition in spring-config.xml will be used, since ApplicationContext is essentially a Map and the most recent definition put in it will win.)



来源:https://stackoverflow.com/questions/16810646/how-to-create-spock-mocks-outside-of-a-specification-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!