2. Spock is Groovy
...and Groovy is Bliss!
def user = new User(firstName: 'Johnny', lastName: 'Walker')
def street = company?.address?.street
users.filter { it.age > 20 }
.sort { it.salary }
.collect { it.lastName + ', ' + it.firstName }
def message = [subject: 'Hello jOpenSpace!', body: 'Meet me at the bar']
Groovy is dense and expressive. Don't
worry it's dynamic - this is a test framework
and tests are executed after every commit!
3. Spock Spec
def "can add an element"() {
given:
def list = new ArrayList<String>()
def element = "Hello Spock"
when:
list.add(element)
then:
list.size() == 1
list.contains(element)
}
Spock test (= "feature method") is
structured into well-known blocks
with defined meaning
4. Assertions
assert name.length() == 6;
expect:
name.length() == 5
java.lang.AssertionError
Condition not satisfied:
assertEquals(6, name.length());
name.length() == 5
|
|
|
Eman 4
false
// hamcrest
assertThat(name.length(), equalTo(6));
// FEST, AssertJ
assertThat(name).hasSize(6);
java.lang.AssertionError:
Expected size:<6> but was:<4> in:
<'Eman'>
Assertions commonly used in
Java are complicated or have
ugly fail messages
Spock makes it simple...
5. Assertions
expect:
rectangle.getArea() == a * b
Condition not satisfied:
rectangle.getArea()
|
|
|
6
Rectangle 2 x 3
== a * b
| | | |
| 4 | 5
|
20
false
…and when something goes
wrong, it writes nice and detailed
fail message
8. Mocking
given:
def sender = Mock(Sender)
Verifying behavior
with mock is simple
when:
println("nothing, hahaha!")
then:
1 * sender.send(_)
Too few invocations for:
1 * sender.send(_)
You can use ranges for invocation
count and wildcards for parameters
(0 invocations)
Unmatched invocations (ordered by similarity):
None
9. Stubbing
given:
def subscriber = Mock(Subscriber)
// pattern matching
subscriber.receive("poison") >> "oh wait..."
subscriber.receive(_) >> "ok"
when:
def response = subscriber.receive("poison")
…and setting expectations
has some powerful features
// returning sequence
subscriber.receive(_) >>> ["ok", "ok", "hey this is too much"]
// computing return value
subscriber.receive(_) >> { String msg -> msg.size() < 10 ? "ok" : "tl;dr" }
10. Data Driven Tests
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
def "maximum of two numbers"() {
expect:
Math.max(a, b) == expectedMax
where:
a << [1, 8, 9]
b << [7, 3, 9]
c << [7, 8, 9]
where:
a | b |
1 | 7 |
8 | 3 |
9 | 9 |
}
expectedMax
7
8
9
}
Spock has first-class
support for
parametrized tests
You can even use
table-like structure
for setting input data
Forget JUnit theories or
TestNG data providers!
11. QuickCheck
def "maximum of
expect:
Math.max(a,
Math.max(a,
Math.max(a,
two numbers"() {
b) == Math.max(b, a)
b) >= a && Math.max(a, b) >= b
b) == a || Math.max(a, b) == b
where:
a << someIntegers()
b << someIntegers()
}
Input data can be
anything Iterable...
…and that makes it simple to
use something like QuickCheck
for generating random data
12. Property-Based Testing
def "sorts a list"() {
when:
def sorted = list.sort(false)
You define invariant
"properties" that hold
true for any valid input
then:
sorted == sorted.sort(false)
sorted.first() == list.min()
sorted.last() == list.max()
sorted.eachWithIndex { x, i -> assert i==0 || sorted[i-1] <= x }
where:
list << someLists(integers())
}
Data-driven tests tend to have
different structure than traditional
"example-based" tests
15. Erik Meijer
Haskell
LINQ
Rx
…and one of the speakers was Erik Meijer,
who was developing Haskell and worked on
LINQ and Reactive Extensions in Microsoft...
17. From
Subject
Spam Filter
Body
=> { ... }
In essence, machine learning is
generating computer code based
on data
E.g. generate a code to recognize
a spam, based on analysis of
bunch of email messages
18. Example-Based TDD
def "can detect spam"() {
expect:
spamDetector(from, subject, body) == isSpam
where:
from
'mtehnik'
'jfabian'
'jnovotny'
'katia777'
|
|
|
|
|
body
'nechces nejaky slevovy kupony na viagra?'
'mels pravdu, tu znelku cz podcastu zmenime'
'penis enlargement - great vacuum pump for you'
'we have nice russian wife for you'
||
||
||
||
||
isSpam
false
false
true
true
}
And that is very similar to TDD. You
are providing more and more test
cases to specify the desired behavior
The difference is, you don't
write the implementation - it is
generated by computer
20. Prepare for the Future
In 10 years, programmers will be replaced by generated code
…and that can have some
impact on job security
21. Generated Code
Machine learning
Genetic programming
There are already some
real-world usages of
generated code
This is an antenna designed
by evolutionary algorithm,
that NASA actually used on
a spacecraft
http://idesign.ucsc.edu/projects/evo_antenna.html
22. Brain Research
Multilayer neural networks
…and our understanding of how
the brain works is getting better
https://www.simonsfoundation.org/quanta/20130723-as-machines-get-smarter-evidence-they-learn-like-us/
23. Property-Based Testing
def "sorts a list"() {
when:
def sorted = list.sort(false)
then:
sorted == sorted.sort(false)
sorted.first() == list.min()
sorted.last() == list.max()
sorted.eachWithIndex { x, i -> assert i==0 || sorted[i-1] <= x }
where:
list << someLists(integers())
}
Now take a second look
at property-based test
It is only matter of time when it will be
cheaper to generate code from
specification than to write it manually
Can there be better specification
for code generator?