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?

Jan Thomä
  • 13,296
  • 6
  • 55
  • 83

4 Answers4

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

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • 4
    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. – Karl the Pagan Sep 12 '13 at 16:15
  • Perhaps SpecificationMixin is what I was looking for? – Karl the Pagan Sep 12 '13 at 16:24
  • 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. – Peter Niederwieser Sep 12 '13 at 19:57
  • @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. – cdeszaq Dec 05 '13 at 16:07
  • > 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. – Farrukh Najmi Jan 24 '22 at 12:40
6

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'
Piotr Góralczyk
  • 557
  • 5
  • 14
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: "")]
}
  • 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 https://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. – Jan Thomä Mar 25 '15 at 08:03
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.)

Jim Moore
  • 46
  • 3