smarter testing with spock

50
Dmitry Voloshko 03/11/2012 Smarter Testing With Spock

Upload: it-weekend

Post on 10-May-2015

2.825 views

Category:

Documents


0 download

DESCRIPTION

by Dmitry Voloshko What we’ll talk about Spock?! State Based Testing Data Driven Testing Interaction Based Testing Spock Extensions More Cool Stuff

TRANSCRIPT

Page 1: Smarter Testing With Spock

Dmitry Voloshko03/11/2012

Smarter Testing With Spock

Page 2: Smarter Testing With Spock

What we’ll talk about

Spock?!

State Based Testing

Data Driven Testing

Interaction Based Testing

Spock Extensions

More Cool Stuff

Page 3: Smarter Testing With Spock

Spock?!

Page 4: Smarter Testing With Spock

Spock is...

A developer testing framework...

for Groovy and Java applications...

based on Groovy...

fully compatible with JUnit...

but going beyond!

Page 5: Smarter Testing With Spock

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!

Page 6: Smarter Testing With Spock

Getting Started

Homepage http://spockframework.org

Source Code https://github.com/spockframework/spock

Spock Web Console http://meet.spockframework.org

Spock Example Project http://downloads.spockframework.orghttps://github.com/spockframework/spock/tree/groovy-1.8/spock-example

Page 7: Smarter Testing With Spock

Who’s Using Spock?Gradle GPars

Grails Geb

Grails Plugin Collective

Apache Tapestry

Griffon SpockSpring?

Page 8: Smarter Testing With Spock

Who’s Using Spock? Betfair be2

bemoko BSkyB CTI DigitalDonewtech Solutions

eHarmony Energized Work Gennemtænkt IT

IntelliGrape Software NetflixSmarter Ecommerce STERMEDIA Sp. z o.o.

Software Projects Spot DivingSystemsForge TouK

Page 9: Smarter Testing With Spock

State Based Testing

Page 10: Smarter Testing With Spock

State Based Testing

Classical Unit TestingArrangeActAssert

Given-When-Then

Page 11: Smarter Testing With Spock

The Code For TestingAccount.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); }}

Page 12: Smarter Testing With Spock

The Code For TestingNegativeAmountWithdrawnException.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; }}

Page 13: Smarter Testing With Spock

Show me the codepublic 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()); }}

Page 14: Smarter Testing With Spock

Show me the codeclass 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); }}

Page 15: Smarter Testing With Spock

Show me the codeclass AccountSpec2 extends Specification {

def "withdraw some amount"() { given: def account = new Account(5.0)

when: account.withdraw(2.0)

then: account.balance == 3.0 }}

Page 16: Smarter Testing With Spock

Show me the codeclass 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 }}

Page 17: Smarter Testing With Spock

Show me the codeclass 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 }}

Page 18: Smarter Testing With Spock

Show me the codeclass 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() }}

Page 19: Smarter Testing With Spock

Show me the codeclass 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() }}

Page 20: Smarter Testing With Spock

Show me the codeclass 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"] }}

Page 21: Smarter Testing With Spock

Recap: State Based TestingBlockssetup: cleanup: expect: given: when: then:where: and:

Fixture Methodssetup() cleanup() setupSpec() cleanupSpec()

Instance and @Shared fields

old() and thrown()

Page 22: Smarter Testing With Spock

Data Driven Testing

Page 23: Smarter Testing With Spock

Data Driven Testing

Test the same behavior...

with varying data!

Page 24: Smarter Testing With Spock

Show me the codeclass AccountSpecDataDriven extends Specification { @Unroll def "withdraw some amount"() { given: def account = new Account(balance)

when: account.withdraw(withdrawn)

then: account.balance == remaining

where: balance | withdrawn | |remaining 5.0 | 2.0 | | 3.0 4.0 | 0.0 | | 4.0 4.0 | 4.0 | | 0.0 }}

Page 25: Smarter Testing With Spock

Show me the codeclass AccountSpecDataDriven extends Specification { @Unroll def "withdrawing #withdrawn from account with balance #balance"() { given: def account = new Account(balance)

when: account.withdraw(withdrawn)

then: account.balance == old(account.balance) – withdrawn

where: balance | withdrawn 5.0 | 2.0 4.0 | 0.0 4.0 | 4.0 }}

Page 26: Smarter Testing With Spock

Show me the codeclass AccountSpecDatabaseDriven extends Specification { @Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")

def setupSpec() { sql.execute("create table accountdata (id int primary key, balance decimal, withdrawn decimal, remaining decimal)" ) sql.execute("insert into accountdata values (1, 5.0, 2.0, 3.0), (2, 4.0, 0.0, 4.0), (3, 4.0, 4.0, 0.0)") }

@Unroll def "withdrawing #withdrawn from account with balance #balance leaves #remaining"() { given: def account = new Account(balance)

when: account.withdraw(withdrawn)

then: account.balance == remaining

where: [balance, withdrawn, remaining] << sql.rows("select balance, withdrawn, remaining from accountdata") }}

Page 27: Smarter Testing With Spock

Recap: Data Driven Testing

where: block

Data tables

External data sources

@Unroll

Page 28: Smarter Testing With Spock

Interaction Based Testing

Page 29: Smarter Testing With Spock

Interaction Based Testing

Design and test how your objectscommunicate

Mocking frameworks to the rescue

Spock comes with its own mocking framework

Page 30: Smarter Testing With Spock

The Code For TestingPublisherSubscriber.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) {} } }}

Page 31: Smarter Testing With Spock

The Code For TestingPublisherSubscriber.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) {} } }}

Page 32: Smarter Testing With Spock

Show me the codeclass 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") }}

Page 33: Smarter Testing With Spock

Show me the codeclass 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") }}

Page 34: Smarter Testing With Spock

Show me the codeclass 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(_) }}

Page 35: Smarter Testing With Spock

Recap: Interaction Based Testing

Creatingdef sub = Mock(Subscriber)Subscriber sub = Mock()

Mocking1 * 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")

Page 36: Smarter Testing With Spock

Recap: Interaction Based Testing

Stubbing// now returns status codeString receive(String msg) { ... }

sub.receive(_) >> "ok"sub.receive(_) >>> ["ok", "ok", "fail"]sub.receive(_) >>> { msg -> msg.size() > 3 ? "ok" : "fail" }

Mocking and Stubbing3 * sub.receive(_) >>> ["ok", "ok", "fail"]

Impressing your friends(_.._) * _._(*_) >> _

Page 37: Smarter Testing With Spock

Spock Extensions

Page 38: Smarter Testing With Spock

Spock Extensions

Listeners

Interceptors

Annotation-driven extensions

Global extensions

Page 39: Smarter Testing With Spock

Built-in Extensions

@Ignore@IgnoreRest@FailsWith@Timeout@AutoCleanup@Stepwise@RevertMetaClass@Rule

Page 40: Smarter Testing With Spock

Show me the codeclass 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() }}

Page 41: Smarter Testing With Spock

Show me the code@Stepwiseclass StepwiseSpec extends Specification { def "step 1"() { expect: true }

def "step 2"() { expect: true }

def "step 3"() { expect: true }}

Page 42: Smarter Testing With Spock

External Extensions

spock-grails

spock-spring

spock-guice

spock-tapestry

spock-unitils

spock-griffon

spock-arquillian

spock-extensions http://github.com/robfletcher/spock-extensions

Page 43: Smarter Testing With Spock

Grails Extensions

http://grails.org/plugin/spock

https://github.com/spockframework/spock-grails

grails install plugin spock 0.6

grails test-app

grails test-app unit:TestSpec

Page 44: Smarter Testing With Spock

Grails Extensions@TestFor(MouthService) @Mock([Patient, PatientMouth, PatientTooth])class MouthServiceSpec extends Specification { def "Get patient tooth by index"() { setup: "Create domain mocks" def patient = new Patient(dateOfBirth: new Date('05/03/1984')).save(false) def patientTooth = new PatientTooth(index: 10, toothCode: "10").save(false) def patientMouth = new PatientMouth(patient: patient, patientTeeth: [patientTooth]).save(false)

when: patientTooth = service.getPatientToothByIndex(patientMouth, index)

then: patientTooth.toothCode == tooth patientMouth.patientTeeth.contains(patientTooth)

where: index | tooth 10 | "10" 20 | null }}

Page 45: Smarter Testing With Spock

Spring Extension

class InjectionExamples extends Specification { @Autowired Service1 byType

@Resource Service1 byName

@Autowired ApplicationContext context}

Page 46: Smarter Testing With Spock

Other Cool Stuff

Page 47: Smarter Testing With Spock

Configuring Spock~/.spock/SpockConfig.groovy, or on class path, or with -Dspock.configuration

runner {filterStackTrace falseinclude Fastexclude SlowoptimizeRunOrder true

}

@Fastclass MyFastSpec extends Specification {

def "I’m fast as hell!"() { expect: true }

@Slow def “Sorry, can’t keep up..."() {expect: false }

}

Page 48: Smarter Testing With Spock

Tooling

Eclipse, IDEA

Ant, Maven, Gradle

Jenkins, Bamboo, TeamCity

Spock runs everywhere JUnit and Groovy run!

Page 49: Smarter Testing With Spock

Spock Under The HoodYou write...a = 1; b = 2; c = 4expect: 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