Advanced Spock Techniques

In recent years there have been a couple of tools that stand out when it comes to helping me be productive. One of those is the groovy test framework Spock. It is worthy of an introductory blog post... but that isn't this post. One of the challenges to Spock is the documentation isn't a complete as one would hope. The Spock Basics is fantastic when getting started but it is what it claims to be basics. I've been speaking with NFJS on the subject of Spock for the last half of 2011. On 2 occasions I've had the look our red shirted friend in the picture has when asked about controlling aspects of mocks in spock... meaning I didn't have the answer. In this case, google didn't discover it either... a quick email to the Peter Niederwieser reveals the previously undocumented solution. (at least that's my understanding)... and thanks Peter!
Here we will discuss 2 advanced aspects of mocking with spock. If you need more details on mocking in general hit the Spock site... or wait patiently for a future post:)
Problem Setup:
void testDeposit() {
Account account = new Account(TEST_ACCOUNT_NO, 100)
def mock = new MockFor(AccountDao)
mock.demand.findAccount(TEST_ACCOUNT_NO) { account }
mock.demand.updateAccount(account) { assertEquals 150, account.balance }
def dao = mock.proxyDelegateInstance()
def service = new AccountServiceImpl(dao)
service.deposit TEST_ACCOUNT_NO, 50
mock.verify dao
}
Spock Cleanup
A similar solution in Spock might look like this:
def "deposit into account test refactor"() {
def mock = Mock(AccountDao)
def service = new AccountServiceImpl(mock)
when:
service.deposit nmr, amt
then:
mock.findAccount(_) >> account
1 * mock.updateAccount(_)
0 * mock.createAccount(_)
and:
account.balance == total
where:
nmr | account | amt | total
"101" | new Account("101", 100) | 50 | 150
"101" | new Account("101", 0) | 50 | 50
}
However this solution doesn't guarantee order nor do we want to use the 0 * mock.
Spock Mock Solution
It turns out that we can repeatedly use the then: dsl to determine the order of events. So we can modify the Spock code above with the following changes.
then:
1 * mock.findAccount("1234") >> account
then:
_ * mock.updateAccount(_)
In this case we are saying that the findAccount() method will be called exactly once and return the account object and "then" the updateAccount will be call any number of times with any type of argument. The test will fail if the updateAccount is ever called prior to the findAccount being invoked first.
We can use this same technique to solve the strictness concern we have as well by adding one more then: block of code as outlined below:
then:
1 * mock.findAccount("1234") >> account
then:
_ * mock.updateAccount(_)
then:
0 * mock._
In this case the last 0 * mock._ says to expect nothing else. Ahhh.. the beauties of a dynamic language:)
Happy Coding... may your builds never fail and your tests always green bar!
About Ken Sipe
Ken has been a practitioner and instructor of RUP since the late 1990s, and an extreme programmer and coach since the middle 2000s. Ken has worked with Fortune 500 companies to small startups in the roles of developer, designer, application architect and enterprise architect. Ken's current focus is on enterprise system automation and continuous delivery systems.
Ken is an international speaker on the subject of software engineering speaking at conferences such as JavaOne, JavaZone, Jax-India, and The Strange Loop. He is a regular speaker with NFJS where he is best known for his architecture and security hacking talks. In 2009, Ken was honored by being awarded the JavaOne Rockstar Award at JavaOne in SF, California and the JavaZone Rockstar Award at JavaZone in Oslo, Norway as the top ranked speaker.
More About Ken »NFJS, the Magazine
May Issue Now AvailableClient-Side MVC with Spine.js, Part 1
by Craig WallsOn Prototypal Inheritance, Part 2
by Raju GandhiMaking use of Scala Lazy Collections
by Venkat SubramaniamIntegration Testing Web Applications Using Gradle
by Kenneth Kousen