4. Spock is...
A developer testing framework...
for Groovy and Java applications...
based on Groovy...
fully compatible with JUnit...
but going beyond!
5. Spock Can...
Reduce the lines of test code
Make tests more readable
Turn tests into specifications
Be extended in powerful ways
Bring back the fun to testing!
11. The Code For Testing
Account.java
public class Account {
private BigDecimal balance = new BigDecimal(0);
public Account(BigDecimal initial) {
balance = initial;
}
public BigDecimal getBalance() {
return balance;
}
public void withdraw(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new NegativeAmountWithdrawnException(amount);
}
balance = balance.subtract(amount);
}
}
12. The Code For Testing
NegativeAmountWithdrawnException.java
public class NegativeAmountWithdrawnException extends RuntimeException {
private final BigDecimal amount;
public NegativeAmountWithdrawnException(BigDecimal amount) {
super("cannot withdraw" + amount);
this.amount = amount;
}
public BigDecimal getAmount() {
return amount;
}
}
13. Show me the code
public class AccountTest {
@Test
public void withdrawSomeAmount() {
// given
Account account = new Account(BigDecimal.valueOf(5));
// when
account.withdraw(BigDecimal.valueOf(2));
// then
assertEquals(BigDecimal.valueOf(3), account.getBalance());
}
}
14. Show me the code
class AccountSpec extends Specification {
def "withdraw some amount"() {
given:
Account account = new Account(BigDecimal.valueOf(5));
when:
account.withdraw(BigDecimal.valueOf(2));
then:
account.getBalance() == BigDecimal.valueOf(3);
}
}
15. Show me the code
class AccountSpec2 extends Specification {
def "withdraw some amount"() {
given:
def account = new Account(5.0)
when:
account.withdraw(2.0)
then:
account.balance == 3.0
}
}
16. Show me the code
class AccountSpec3 extends Specification {
def "withdraw some amount"() {
given: "an account with a balance of five euros"
def account = new Account(5.0)
when: "two euros are withdrawn"
account.withdraw(2.0)
then: "three euros remain in the account"
account.balance == 3.0
}
}
17. Show me the code
class AccountSpec4 extends Specification {
def "can't withdraw a negative amount"() {
given:
def account = new Account(5.0)
when:
account.withdraw(-1.0)
then:
NegativeAmountWithdrawnException e = thrown()
e.amount == -1.0
}
def "withdrawing some amount decreases the balance by exactly that amount"() {
def account = new Account(5.0)
when:
account.withdraw(2.0)
then:
account.balance == old(account.balance) - 2.0
}
}
18. Show me the code
class SharedField extends Specification {
@Shared File file
def "a file based test"() {
when:
file = new File("foo.txt")
file.createNewFile()
then:
file.exists()
}
def "by now the object is not null"() {
expect:
file.exists()
cleanup:
file.delete()
}
}
19. Show me the code
class SetupSpecSpec extends Specification {
File file
def setup() {
file = new File("foo2.txt")
}
def "a file based test"() {
when:
file.createNewFile()
then:
file.exists()
}
def "by now the object is not null"() {
expect: file.exists()
cleanup: file.delete()
}
}
20. Show me the code
class Conditions extends Specification {
def "when-then style"() {
when:
def x = Math.max(5, 9)
then:
x == 9
}
def "expect style"() {
expect:
Math.max(5, 9) == 9
}
def "more complex conditions"() {
expect:
germanCarBrands.any { it.size() >= 3 }
}
private getGermanCarBrands() { ["audi", "bmw", "porsche"] }
}
21. Recap: State Based Testing
Blocks
setup: cleanup: expect: given: when: then:
where: and:
Fixture Methods
setup() cleanup() setupSpec() cleanupSpec()
Instance and @Shared fields
old() and thrown()
29. Interaction Based Testing
Design and test how your objects
communicate
Mocking frameworks to the rescue
Spock comes with its own mocking
framework
30. The Code For Testing
PublisherSubscriber.groovy
interface Subscriber {
void receive(String message)
}
class Publisher {
List<Subscriber> subscribers = []
void publish(String message) {
for (subscriber in subscribers) {
try {
subscriber.receive(message)
} catch (ignored) {}
}
}
}
31. The Code For Testing
PublisherSubscriber.groovy
interface Subscriber2 {
void receive(String message)
boolean isActive()
}
class Publisher2 {
List<Subscriber2> subscribers = []
void publish(String message) {
for (subscriber in subscribers) {
try {
if (subscriber.active) {
subscriber.receive(message)
}
} catch (ignored) {}
}
}
}
32. Show me the code
class PublisherSubscriberSpec extends Specification {
def pub = new Publisher()
def sub1 = Mock(Subscriber)
def sub2 = Mock(Subscriber)
def setup() {
pub.subscribers << sub2 << sub1
}
def "delivers messages to all subscribers"() {
when:
pub.publish("msg")
then:
1 * sub1.receive("msg")
1 * sub2.receive("msg")
}
}
33. Show me the code
class PublisherSubscriberSpec2 extends Specification {
def pub = new Publisher()
def sub1 = Mock(Subscriber)
def setup() {
pub.subscribers << sub1
}
def "delivers messages in the order they are published"() {
when:
pub.publish("msg1")
pub.publish("msg2")
then:
1 * sub1.receive("msg1")
then:
1 * sub1.receive("msg2")
}
}
34. Show me the code
class PublisherSubscriber2Spec extends Specification {
def pub = new Publisher2()
def sub1 = Mock(Subscriber2)
def sub2 = Mock(Subscriber2)
def setup() {
pub.subscribers << sub1 << sub2
}
def "delivers messages to all active subscribers"() {
sub1.active >> true
sub2.active >> false
when:
pub.publish("msg")
then:
1 * sub1.receive("msg")
0 * sub2.receive(_)
}
}
35. Recap: Interaction Based Testing
Creating
def sub = Mock(Subscriber)
Subscriber sub = Mock()
Mocking
1 * sub.receive("msg")
(1..3) * sub.receive(_)
(1.._) * sub.receive(_ as String)
1 * sub.receive(!null)
1 * sub.receive({it.contains("m")})
1 * _./rec.*/("msg")
40. Show me the code
class JUnitRules extends Specification {
@Rule TemporaryFolder tempFolder
@Shared File file
def "a file based test"() {
when:
file = tempFolder.newFile("foo.txt")
then:
file.exists()
}
def "by now the file has been deleted"() {
expect:
!file.exists()
}
}
41. Show me the code
@Stepwise
class StepwiseSpec extends Specification {
def "step 1"() {
expect: true
}
def "step 2"() {
expect: true
}
def "step 3"() {
expect: true
}
}
47. Configuring Spock
~/.spock/SpockConfig.groovy, or on class path, or with -Dspock.configuration
runner {
filterStackTrace false
include Fast
exclude Slow
optimizeRunOrder true
}
@Fast
class MyFastSpec extends Specification {
def "I’m fast as hell!"() { expect: true }
@Slow
def “Sorry, can’t keep up..."() {expect: false }
}
49. Spock Under The Hood
You write...
a = 1; b = 2; c = 4
expect: sum(a, b) == c
Spock generates...
def rec = new ValueRecorder()
verifier.expect(
rec.record(rec.record(sum(rec.record(a),
rec.record(b)) == rec.record(c)))
You see...
sum (a, b) == c
| | | | |
3 1 2 | 4
false