Spock Testing Tool
48 Hours With Spock
Spock on Trial
Ok, only had 2 days to learn as much as possible about a testing tool. Chose Spock as it is groovy-based and i am into the groovy eco-system now with groovy, gradle, gaelyf, grails, caelyf and the new kid on the block called ‘grain’. Here’s a list of references i found to be most useful in getting up to speed with spock.
- see: http://code.google.com/p/spock/wiki/SpockBasics – a bit like a wiki overview of what Spock is, what it can do and some of the vocabulary you’ll need to know.
- see: http://meetspock.appspot.com/ – the Spock web console where we can try spock using a web service console rather than installing from scratch
- see: https://github.com/hlship/spock-examples/blob/master/src/test/groovy/org/example/ExampleSpecification.groovy – a folder of some sample Spock specifications
- see: http://spockframework.github.io/spock/docs/1.0/index.html – Spock Framework Reference Documentation, or the bible of Spock syntax
A gal that writes clearly on the plus points and perils of Spock, and testing in general, is Trisha Gee. Here are several of her spock-related posts :
- see: http://mechanitis.blogspot.co.uk/2013/07/spock-is-awesome-seriously-simplified.html – Spock is awesome! Seriously Simplified Mocking
- see: http://mechanitis.blogspot.co.uk/2013/12/spock-data-driven-testing.html – Trish’s data driven test tables
- see: http://mechanitis.blogspot.co.uk/2013/07/spock-passes-next-test-painless-stubbing.html – Trish’s painless stubs
If you prefer a set of slides to walk you thru the Spock framework, try this. It’s a bit deep for beginners but you’ll need it after you get your feet wet.
…
…
- see: http://www.slideshare.net/hlship/spock-a-highly-logical-way-to-test and a mocking framework is discussed at slide 39+
- along with cardinality like
then: 1 * dao.getById(123) >> null illegalArgumentException e = thrown() e.message == "No customer 123" 1 * dao.getById(123) >> null ^ cardinality/invocations ^ target method ^ argument ^ return value
Yes and no I don’t understand cardinality yet myself, so we’ll leave it till later.
MyFirstSpecification.groovy
There is always a first try at anything. Here’s a script written in groovy as we took Spock out for a test drive. There are plenty of features that work as well as some that don’t. Copy this into your groovyConsole (or try the Spock web console, but leave of the ‘Grapes(…) bits).
The ‘Grapes’ will grab the spock jar from a maven repo. This will mean you only need an installed groovy engine.
@Grapes(
@Grab(group='org.spockframework', module='spock-core', version='0.7-groovy-2.0')
)
import spock.lang.*
class MyFirstSpecification extends Specification {
// fields
def static num= 0
def fh = null;
def balance = 1.25
// fixture methods
/*
Fixture methods are responsible for setting up and cleaning up the environment
in which feature methods are run. Usually it's a good idea to use a fresh fixture
for every feature method, which is what the setup() and cleanup() methods are for.
Occasionally it makes sense for feature methods to share a fixture, which is achieved
by using shared fields together (see below) with the setupSpec() and cleanupSpec() methods.
All fixture methods are optional.
The setupSpec() and cleanupSpec() methods may not reference instance fields, only @Shared and statics.
One fixture methos will typically:
1) create instance of Specification
2) invoke setup()
3) invoke feature method
4) invoke cleanup()
*/
def setup() { println "setup() num="+num; } // run before every feature method
def cleanup() {println "cleanup num="+num; ++num; } // run after every feature method
def setupSpec() { println "// run before the first feature method" } // run before the first feature method
def cleanupSpec() { println "// run after the last feature method" } // run after the last feature method
// feature methods
/*
Feature methods are the heart of a specification.Conceptually, a feature method consists of four phases:
Set up the feature's fixture
Provide a stimulus to the system under specification
Describe the response expected from the system
Clean up the feature's fixture
Whereas the first and last phases are optional, the stimulus and response phases are always present
(except in interacting feature methods), and may occur more than once.
*/
def "increment the counter"()
{
setup: "open a file connection"
// code goes here
fh = new File("/Volumes/FHD-XS/spock");
and: "seed the customer table"
// code goes here
when:
num += 1;
then:
assert num == 1, "increment counter failed" // power assert with "added notes" after condition
//where:
// blocks go here
} // end of increment
/*
Specifications as Documentation
Well-written specifications are a valuable source of information.
Especially for higher-level specifications targeting a wider audience
than just developers (architects, domain experts, customers, etc.),
it makes sense to provide more information in natural language than
just the names of specifications and features. Therefore, Spock provides
a way to attach textual descriptions to blocks:
*/
//@Ignore
def "In Behavior Driven Development, customer-facing features (called stories) are described in a given-when-then format"()
{
given: "an empty bank account" // alias for setup:
// ...
balance = 0.00
when: "the account is credited \$10"
// ...
balance += 10.01
then: "the account's balance is \$10"
// ...
balance == 10.01
} // end of def
/* the 'expect:' choice combines stimulus & response - best for no side-effect functions
expect: sky.color == "blue"
stimulus response
---------
when: then: combo used as a pair and work if methods have side effects;
then: only for conditions,exceptions, interactions and var defs
def "clouds are grey"(){
given: "a fresh object"
def sky = new Sky()
when: sky.addStormSystem() // since this method does not exist, test will fail
then: sky.color == "grey"
} // end of def
---------
// example of the 'old' keyword
def "push ele onto stack"(){
def stack = new Stack()
when: stack.push("ele")
then: stack.size() == old(stack.size()) + 1
} // end of def
--------
// extended asserts
def "try asserts with comments"(){
expect: assert 4==5, "naw, this won't work !"
} // end of def
*/
// where: block using lists
def "can you understand this ?"()
{
expect:
name.size() == length
where:
name << ["Kirk","Spock","Scotty"]
length true
when:
checker.isValid()
then :
assert checker.isValid() == true, "my dog has flees"
} // end of def
// helper methods
}
Sysout Terminal Session
// test results from syslog:
// run before the first feature method
setup() num=0
cleanup num=1
setup() num=2
cleanup num=2
setup() num=3
cleanup num=3
setup() num=4
cleanup num=4
setup() num=5
cleanup num=5
setup() num=6
cleanup num=6
setup() num=7
cleanup num=7
setup() num=8
cleanup num=8
setup() num=9
cleanup num=9
setup() num=10
cleanup num=10
setup() num=11
cleanup num=11
// run after the last feature method
JUnit 4 Runner, Tests: 7, Failures: 4, Time: 457
Test Failure: increment the counter(MyFirstSpecification)
Condition not satisfied:
num == 0
increment counter failed
at MyFirstSpecification.increment the counter(SpockSpecifications.groovy:86)
Test Failure: length of crew member names(MyFirstSpecification)
Condition not satisfied:
name.size() == length
| | | |
Kirk 4 | 5
false
at MyFirstSpecification.length of crew member names(SpockSpecifications.groovy:181)
Test Failure: might fail(MyFirstSpecification)
Expected exception java.lang.IllegalStateException, but no exception was thrown
at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:79)
at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:60)
at MyFirstSpecification.might fail(SpockSpecifications.groovy:207)
Test Failure: stubbing a global good to end of method(MyFirstSpecification)
groovy.lang.MissingPropertyException: No such property: Person for class: MyFirstSpecification
at MyFirstSpecification.stubbing a global good to end of method(SpockSpecifications.groovy:220)
Result: org.junit.runner.Result@a064fb
Live long and prosper …
Stay tuned for part two when we will decode the vulcan terminology and explore some of the mysteries that is Spock.