How to create Spock mocks outside of a specification class?
Asked Answered
E

4

14

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?

Emptyhanded answered 29/5, 2013 at 9:48 Comment(0)
L
6

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 */ }.

Lozoya answered 29/5, 2013 at 10:28 Comment(5)
I'm sitting in the SpringOne Spock & testMvc session and it's starting to feel obvious that in order to mix in some services mocked, and some services configured by Spring (for example to avoid having integration logic outside of the test) this feature would be very useful.Lyell
Perhaps SpecificationMixin is what I was looking for?Lyell
I'm not familiar with SpecificationMixin. There is a pull request in the works that allows external construction of mocks, and hopefully it will ship with the next Spock version.Lozoya
@PeterNiederwieser - Any link you might be able to share for us to track the progress more closely? This would be a killer feature for java-heavy shops that use a lot of Spring to adopt Spock more quickly.Fiddlewood
> Creating mocks outside a spec class (and using them in another spec class) isn't currently possible. @PeterNiederwieser Is ^^^ still true. It seems that since Spock 1.1 the DetachedMockFactory and MockUtil.attach/detachMock seem to support it. But I am having trouble getting it to work.Stannic
H
7

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'
Hassock answered 25/3, 2017 at 17:6 Comment(0)
L
6

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 */ }.

Lozoya answered 29/5, 2013 at 10:28 Comment(5)
I'm sitting in the SpringOne Spock & testMvc session and it's starting to feel obvious that in order to mix in some services mocked, and some services configured by Spring (for example to avoid having integration logic outside of the test) this feature would be very useful.Lyell
Perhaps SpecificationMixin is what I was looking for?Lyell
I'm not familiar with SpecificationMixin. There is a pull request in the works that allows external construction of mocks, and hopefully it will ship with the next Spock version.Lozoya
@PeterNiederwieser - Any link you might be able to share for us to track the progress more closely? This would be a killer feature for java-heavy shops that use a lot of Spring to adopt Spock more quickly.Fiddlewood
> Creating mocks outside a spec class (and using them in another spec class) isn't currently possible. @PeterNiederwieser Is ^^^ still true. It seems that since Spock 1.1 the DetachedMockFactory and MockUtil.attach/detachMock seem to support it. But I am having trouble getting it to work.Stannic
O
5

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: "")]
}
Ordway answered 22/3, 2015 at 10:9 Comment(1)
That is a neat workaround :). In the meantime we have moved to a more lightweight approach though. We try to stay away completely from building spring contexts in our unit tests and simply mock out all the dependencies ourselves (see gist.github.com/derkork/45d7fba64b54a41608e1). This has significantly increased our test throughput. We only use spring for complex things like DAO tests where we shoot real statements against an in-memory database.Upright
Z
0

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

Zachariahzacharias answered 15/9, 2015 at 22:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.