SlideShare a Scribd company logo
1 of 89
Download to read offline
Smarter testing Java code
with Spock Framework
Including changes in Spock 1.0
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Warszawa, 20th April 2015
About me
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
2 interesting companies
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Agenda
Why I like Spock?
Less know, but cool Spock features
New and noteworthy in Spock 1.0
Spock tricks
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Why idea for this presentation?
JUnit/TestNG + Mockito + AssertJ work...
... but Spock brings testing to another level
Integrates seamlessly with production Java code
Since version 1.0 provides even more useful features
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Why idea for this presentation?
JUnit/TestNG + Mockito + AssertJ work...
... but Spock brings testing to another level
Integrates seamlessly with production Java code
Since version 1.0 provides even more useful features
Encourage more people to using Spock
In even more effective way
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Why I like Spock?
Consist and readable test code
Tests as specification by default
All Groovy magic available to help
Embedded mocking framework
Although Mockito can be used if preferred
Highly extensible
Compatible with tools supporting JUnit
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple specification in Spock
given/when/then (or expect) are required to compile code
class SimpleCalculatorSpec extends Specification {
def "should add two numbers"() {
given:
def sut = new Calculator()
when:
def result = sut.add(1, 2)
then:
result == 3
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Conditional test exection
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@Requires
Runs annotated test (or the whole specification) only if
given criteria are met
Useful to depend on JVM version, operating system,
activated profile, custom system property, etc.
Opposite to @IgnoreIf
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@Requires
Runs annotated test (or the whole specification) only if
given criteria are met
Useful to depend on JVM version, operating system,
activated profile, custom system property, etc.
Opposite to @IgnoreIf
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
1.0
@Requires - continued
Provides access to system properties and environment
variables
@Requires({ System.getProperty("os.arch") == "amd64" })
def "should use optimization on 64-bit systems"() { ... }
@Requires({ env.containsKey("ENABLE_CRM_INTEGRATION_TESTS") })
def "should do complicated things with CRM"() { ... }
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@Requires - continued
Can use static methods
import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable
@Requires({ isUrlAvailable("http://some-host.intranet/") })
class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification {
//could be also from trait or other class
static boolean isUrlAvailable(String url) {
//...
}
def "should do one thing in intranet"() { ... }
def "should do another thing in intranet"() { ... }
...
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@Requires - continued
Can use static methods
import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable
@Requires({ isUrlAvailable("http://some-host.intranet/") })
class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification {
//could be also from trait or other class
static boolean isUrlAvailable(String url) {
//...
}
def "should do one thing in intranet"() { ... }
def "should do another thing in intranet"() { ... }
...
}
Import is required to prevent MissingMethodException
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@Requires - continued
Can use static methods
import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable
@Requires({ isUrlAvailable("http://some-host.intranet/") })
class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification {
@Memoized
//could be also from trait or other class
static boolean isUrlAvailable(String url) {
//...
}
def "should do one thing in intranet"() { ... }
def "should do another thing in intranet"() { ... }
...
}
@Memoizedfor caching
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@IgnoreIf and @Requires - new features
spock.util.environment.Jvm
Simple and convenient access to Java version
(java.versionand java.specification.version)
boolean isJava7() //true only if Java 7
boolean isJava8Compatible() //true if Java 8+
String getJavaVersion() //e.g. "1.8.0_05"
String getJavaSpecificationVersion() //e.g. "1.8"
@IgnoreIf({ !jvm.java8Compatible })
def "should find at runtime and use CompletableFuture for Java 8+"() {
...
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@IgnoreIf and @Requires - new features
spock.util.environment.Jvm
Simple and convenient access to Java version
(java.versionand java.specification.version)
boolean isJava7() //true only if Java 7
boolean isJava8Compatible() //true if Java 8+
String getJavaVersion() //e.g. "1.8.0_05"
String getJavaSpecificationVersion() //e.g. "1.8"
@IgnoreIf({ !jvm.java8Compatible })
def "should find at runtime and use CompletableFuture for Java 8+"() {
...
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
1.0
@IgnoreIf and @Requires - new features
spock.util.environment.OperatingSystem
Simple and convenient access to OS information
(os.nameand os.versionsystem properties)
String getName() //Linux
String getVersion() //8.1
Family getFamily() //SOLARIS (enum)
boolean isLinux()
boolean isWindows()
boolean isMacOs() //also Solaris and Other
boolean iSolaris()
boolean isOther()
@Requires({ os.linux || os.macOs })
def "should use fancy console features"() { ... }
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@IgnoreIf and @Requires - new features
spock.util.environment.OperatingSystem
Simple and convenient access to OS information
(os.nameand os.versionsystem properties)
String getName() //Linux
String getVersion() //8.1
Family getFamily() //SOLARIS (enum)
boolean isLinux()
boolean isWindows()
boolean isMacOs() //also Solaris and Other
boolean iSolaris()
boolean isOther()
@Requires({ os.linux || os.macOs })
def "should use fancy console features"() { ... }
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
1.0
Cleaning up
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@RestoreSystemProperties
Restores original system properties
From System.getProperties()
To a state before test
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@RestoreSystemProperties - example
@Stepwise
class RestoreSystemPropertiesSpec extends Specification {
@RestoreSystemProperties
def "should deny perform action when running as root"() {
given:
System.setProperty("user.name", "root")
and:
def sut = new DangerousActionPerformer()
when:
sut.squashThemAll()
then:
thrown(IllegalStateException)
}
def "should perform action when running as 'normal' user"() {
given:
def sut = new DangerousActionPerformer()
when:
sut.squashThemAll()
then:
noExceptionThrown()
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@RestoreSystemProperties
Restores original system properties
From System.getProperties()
To a state before test
Useful in integration tests when playing with them
E.g. testing Spring profiles activation based on system
property
Two modes
For one feature (test)
For the whole specification
Just like using for every test
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@RestoreSystemProperties
Restores original system properties
From System.getProperties()
To a state before test
Useful in integration tests when playing with them
E.g. testing Spring profiles activation based on system
property
Two modes
For one feature (test)
For the whole specification
Just like using for every test
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
1.0
@AutoCleanup
Cleans up resource at the end of its life time
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@AutoCleanup - with separate cleanup
class FancyDBSpec extends Specification {
@Shared
private DBConnection dbConnection = new H2DBConnection(...)
def cleanupSpec() {
dbConnection.release()
}
def "should do fancy things on DB"() {
...
}
def "should do fancy2 things on DB"() {
...
}
}
interface DBConnection {
...
void release();
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@AutoCleanup - example
class FancyDBSpec extends Specification {
@AutoCleanup("release")
@Shared
private DBConnection dbConnection = new H2DBConnection(...)
def "should do fancy things on DB"() {
...
}
def "should do fancy2 things on DB"() {
...
}
}
interface DBConnection {
...
void release();
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@AutoCleanup
Cleans up resource at the end of its life time
Applied on field
No need to use separate cleaup/cleanupSpecmethod
Supports both instance (per feature) and
shared (per specification) variables
By default method close()is called
Can be overridden with @AutoCleanup("release")
@AutoCleanup(quiet = true)to suppress exception logging
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@AutoCleanup
Cleans up resource at the end of its life time
Applied on field
No need to use separate cleaup/cleanupSpecmethod
Supports both instance (per feature) and
shared (per specification) variables
By default method close()is called
Can be overridden with @AutoCleanup("release")
@AutoCleanup(quiet = true)to suppress exception logging
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
0.7
Initialization and clean up in test
setup/cleanupcan be declared per feature
useful if feature specific operations
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Initialization and clean up in test
setup/cleanupcan be declared per feature
useful if feature specific operations
def "complex logic should work with fixed thread pool"() {
setup: //alias for 'given:'
ExecutorService threadPool = Executors.newFixedThreadPool(1)
when:
String returnedValue = threadPool
.submit({ "val" } as Callable)
.get(1, TimeUnit.SECONDS)
then:
returnedValue == "val"
cleanup:
threadPool?.shutdown()
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Initialization and clean up in test
setup/cleanupcan be declared per feature
useful if feature specific operations
setupalias for given
cleanuplike finallyexecuted even on exception
works for every iteration (where)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Native support for JUnit before/after
annotations
By default setupSpec(), setup(), cleanup(), cleanupSpec()
In addition @BeforeClass, @Before, @After, @AfterClass
Can be mixed together (also through class hierarchy)
No longer called twice in super class
More than one method of given type can be provided
Useful when dealing with
Additional testing frameworks like in Grails
Traits with setup/clean up code
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
1.0
Simplifying specifications with traits
Share common logic across different specifications
No specification inheritance involved
Can provide setup/cleanup methods
Can have instance variables
Groovy 2.3+ is required
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
G
roovy2.3
Simplifying specifications with traits
trait DatabaseTrait {
@Shared
DBConnection dbConnection
def setupSpec() { //or @BeforeClass
//prepare DB
}
def cleanupSpec() { //or @AfterClass
//clean up DB
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simplifying specifications with traits
trait DatabaseTrait {
@Shared
DBConnection dbConnection
def setupSpec() { //or @BeforeClass
//prepare DB
}
def cleanupSpec() { //or @AfterClass
//clean up DB
}
}
class Fancy1DatabaseSpec extends Specification implements DatabaseTrait {
def "should do fancy1 thing on database"() {
//fancy1 things on database
}
}
class Fancy2DatabaseSpec extends Specification implements DatabaseTrait {
def "should do fancy2 thing on database"() {
//fancy2 things on database
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - basic case
def "should add two integers"() {
given:
Calculator sut = new SimpleCalculator()
when:
int result = sut.add(x, y)
then:
result == expectedResult
where:
x | y || expectedResult
1 | 2 || 3
-2 | 3 || 1
-1 | -2 || -3
}
build-in support with wherekeyword
does not stop on failure for given test case
syntatic sugar for table-like data formatting
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - could look better
private PESELValidator sut = new FakePESELValidator()
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - could look better
private PESELValidator sut = new FakePESELValidator()
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - could look better
private PESELValidator sut = new FakePESELValidator()
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
one combined test for all test cases
not very readable in case of failure(s)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll
@Unroll
def "should validate PESEL correctness (multiple tests)"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll
@Unroll
def "should validate PESEL correctness (multiple tests)"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll
@Unroll
def "should validate PESEL correctness (multiple tests)"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
@Unrollto generate separate test per test case
still confusing in case of failure
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll + #pesel
@Unroll
def "should validate PESEL (#pesel) correctness (#isValid)"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll + #pesel
@Unroll
def "should validate PESEL (#pesel) correctness (#isValid)"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll + #pesel
@Unroll
def "should validate PESEL (#pesel) correctness (#isValid)"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
}
#peseland #isValidto put input parameter into test report
more readable, but still can be confusing for business
people reading BDD reports
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll with message
@Unroll("PESEL '#pesel' should be #description")
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
description = isValid ? "valid" : "invalid"
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll with message
@Unroll("PESEL '#pesel' should be #description")
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
description = isValid ? "valid" : "invalid"
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - @Unroll with message
@Unroll("PESEL '#pesel' should be #description")
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel || isValid
"123" || false
"abcd" || false
"97110208631" || true
description = isValid ? "valid" : "invalid"
}
distinguish test name
and test description
used in test report
additional (artificial) parameter for test report only
with autocompletion in IntelliJ IDEA
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - data pipes
@Unroll("PESEL '#pesel' should be #description")
def "should validate PESEL correctness"() {
expect:
sut.validate(pesel) == isValid
where:
pesel << ["123", "abcd", "97110208631"]
isValid << [false, false, true]
description = isValid ? "valid" : "invalid"
}
table is syntatic sugar for data pipes
"<<" operator to connect data variable with data provider
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parametrized tests - more data providers
@Unroll("#pesel is valid (#dbId)")
def "should validate PESEL correctness (CSV)"() {
expect:
sut.validate(pesel)
where:
[dbId, _, _, pesel] << readValidPeopleFromCSVFile()
.readLines().collect { it.split(',') }
//ugly way to read CSV - don't do this
}
not only static elements
everything iterable in Groovy
also SQL rows or CSV files
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Mocking
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing
class DaoSpec extends Specification {
def "should stub method call"() {
given:
Dao dao = Stub(Dao)
dao.getCount() >> 1
expect:
dao.getCount() == 1
}
}
interface Dao {
int getCount()
Item save(Item item)
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - inline
class DaoSpec extends Specification {
private Dao<Item> dao = Stub(Dao) {
dao.getCount() >> 1
}
def "should stub method call"() {
expect:
dao.getCount() == 1
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - consecutive calls
class DaoSpec extends Specification {
def "should stub consecutive calls"() {
given:
Dao<Item> dao = Stub(Dao)
dao.getCount() >> 1 >> 2
expect:
dao.getCount() == 1
and:
dao.getCount() == 2
and:
dao.getCount() == 2
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - consecutive calls (list)
class DaoSpec extends Specification {
def "should stub consecutive calls (list)"() {
given:
Dao<Item> dao = Stub(Dao)
dao.getCount() >>> [1, 2]
expect:
dao.getCount() == 1
and:
dao.getCount() == 2
and:
dao.getCount() == 2
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - input parameters (array)
class DaoSpec extends Specification {
def "should stub method call to return input value"() {
given:
Item baseItem = new Item()
and:
Dao<Item> dao = Stub(Dao)
dao.save(_) >> { it[0] }
when:
Item returnedItem = dao.save(baseItem)
then:
baseItem.is(returnedItem)
}
}
by default array of input parameters
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - named input parameters
class DaoSpec extends Specification {
def "should stub method call to return input value"() {
given:
Item baseItem = new Item()
and:
Dao<Item> dao = Stub(Dao)
dao.save(_) >> { Item item -> item }
when:
Item returnedItem = dao.save(baseItem)
then:
baseItem.is(returnedItem)
}
}
can be declared explicit as named (and typed) parameters
all have to be declared
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - throwing exception
class DaoSpec extends Specification {
def "should throw exception for specific input parameters"() {
given:
Dao<Item> dao = Stub(Dao)
dao.save(_) >> { Item item ->
throw new IllegalArgumentException(item.toString())
}
when:
dao.save(new Item())
then:
thrown(IllegalArgumentException)
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple Stubbing - throwing exception
class DaoSpec extends Specification {
def "should throw exception for specific input parameters"() {
given:
Dao<Item> dao = Stub(Dao)
dao.save(_) >> { Item item ->
throw new IllegalArgumentException(item.toString())
}
when:
dao.save(new Item())
then:
thrown(IllegalArgumentException)
when:
dao.save(null)
then:
thrown(IllegalArgumentException)
}
}
smart toString()from Groovy (for null objects)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Stubbing and verifying together
class DaoSpec extends Specification {
def "should stub and verify"() {
given:
Item baseItem = new Item()
and:
Dao<Item> dao = Stub(Dao)
when:
Item returnedItem = dao.save(baseItem)
then:
1 * dao.save(_) >> { Item item -> item }
and:
baseItem.is(returnedItem)
}
}
has to be in the same statement
in thensection
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Overriding stubbed value
class BusinessServiceSpec extends Specification {
private BoringCollaborator boringStub = Stub(BoringCollaborator) {
sendPing() >> "OK"
}
private BusinessService sut = new BusinessService(boringStub)
def "should calculate important business case 1"() {
when:
int result = sut.calculateImportantThings()
then:
result == EXPECTED_RESULT_1
}
def "should calculate important business case 2"() { ... }
many positive cases with sendPing() >> "OK"
how to test negative scenario?
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Overriding stubbed value - broken
class BusinessServiceSpec extends Specification {
private BoringCollaborator boringStub = Stub(BoringCollaborator) {
sendPing() >> "OK"
}
private BusinessService sut = new BusinessService(boringStub)
def "should calculate important business case 1"() { ... }
def "should calculate important business case 2"() { ... }
@Ignore("broken")
def "should cover collaborator error"() {
given:
boringStub.sendPing() >> "ERROR" //does not work!
when:
sut.calculateImportantThings()
then:
thrown(IllegalStateException)
}
override in Mockito style does not work
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Overriding stubbed value - in thensection
class BusinessServiceSpec extends Specification {
private BoringCollaborator boringStub = Stub(BoringCollaborator) {
sendPing() >> "OK"
}
private BusinessService sut = new BusinessService(boringStub)
def "should calculate important business case 1"() { ... }
def "should calculate important business case 2"() { ... }
def "should cover collaborator error"() {
when:
sut.calculateImportantThings()
then:
boringStub.sendPing() >> "ERROR" //stubbing in "then", looks bad
and:
thrown(IllegalStateException)
}
has to placed in thensection
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Verification in order - issue
@Ignore("broken")
def "notification should be send after business operation (ignore order)"() {
given:
Dao dao = Mock()
Notifier notifier = Mock()
and:
def sut = new BusinessService(dao, notifier)
when:
sut.processOrder()
then:
1 * dao.saveOrder(_) //order does not matter
1 * notifier.sendNotification(_)
}
order is not verified
notification could be send before saving order
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Verification in order - solution
def "notification should be send after business operation"() {
given:
Dao dao = Mock()
Notifier notifier = Mock()
and:
def sut = new BusinessService(dao, notifier)
when:
sut.processOrder()
then:
1 * dao.saveOrder(_)
then:
1 * notifier.sendNotification(_)
}
mutiple then block are evaluated separately
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Verification in order - gotcha
@Ignore("broken")
def "notification should be send after business operation (with 'and')"() {
given:
Dao dao = Mock()
Notifier notifier = Mock()
and:
def sut = new BusinessService(dao, notifier)
when:
sut.processOrder()
then:
1 * dao.saveOrder(_)
and: //'and' does not verify execution order
1 * notifier.sendNotification(_)
}
anddoes not verify execution order
it is for documentation purposes only
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Miscellaneous
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting
PoolingConditions
Active waiting for condition to pass - like in Awaitility
No more sleep(3000)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting - example
def "should receive packet after while"() {
given:
def conditions = new PollingConditions()
def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade()
when:
asynchronousMessageQueue.sendPing()
then:
conditions.eventually {
assert asynchronousMessageQueue.numberOfReceivedPackets == 1
}
}
With default values
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting - example
def "should receive packet after while"() {
given:
def conditions = new PollingConditions(timeout: 2, initialDelay: 0.3)
def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade()
when:
asynchronousMessageQueue.sendPing()
then:
conditions.eventually {
assert asynchronousMessageQueue.numberOfReceivedPackets == 1
}
}
With custom configuration
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting - example
def "should receive packet after while"() {
given:
def conditions = new PollingConditions(timeout: 2, initialDelay: 0.3)
def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade()
when:
asynchronousMessageQueue.sendPing()
then:
conditions.within(5) {
assert asynchronousMessageQueue.numberOfReceivedPackets == 1
}
}
withing(timeoutInSeconds)can override original timeout in
specific call
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting - example
def "should receive packet after while"() {
given:
PollingConditions conditions = new PollingConditions()
def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade()
when:
asynchronousMessageQueue.sendPing()
then:
conditions.eventually {
asynchronousMessageQueue.numberOfReceivedPackets == 1
}
}
assertkeyword can omitted when PollingConditionsis
declared explicit (not with def)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting
PoolingConditions
Active waiting for condition to pass - like in Awaitility
No more sleep(3000)
Ability to specify: timeout, initialDelay, delay and factor
assertkeyword is (usually) required
Useful in multithreading code and asynchronous external
calls (like message queues)
Cannot be used for interaction verification
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting
PoolingConditions
Active waiting for condition to pass - like in Awaitility
No more sleep(3000)
Ability to specify: timeout, initialDelay, delay and factor
assertkeyword is (usually) required
Useful in multithreading code and asynchronous external
calls (like message queues)
Cannot be used for interaction verification
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
0.7
Asserting previous value (manual way)
Check if/how given value was changed
def "should create new record when item is saved (manual way)"() {
given:
Dao dao = new DummyDao()
and:
int originalNumberOfRecords = dao.getCount()
when:
dao.save(new Item())
then:
dao.getCount() == originalNumberOfRecords + 1
}
interface Dao {
int getCount()
void save(Item item)
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Asserting previous value (Spock way)
def "should create new record when item is saved"() {
given:
Dao dao = new DummyDao()
when:
dao.save(new Item())
then:
dao.getCount() == old(dao.getCount()) + 1
}
interface Dao {
int getCount()
void save(Item item)
}
Spock generates code to keep the old value
Works with fields and methods
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
BDD-like documentation
Highlight business context
Block description (when: "put element on stack")
@See("https://en.wikipedia.org/wiki/Spock")
@Issue("http://tinyurl.com/spock-issue260")
@Subject("TODO: przyklady")
@Title
@Narrative
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Optional runtime configuration
Configures Spock behavior
Injectable into run context and extensions
By default for
Runner
Report
Ability to create custom configuration when needed
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
0.7, 1.0
Optional runtime configuration
~/.spock/SpockConfig.groovy
runner {
include package.DefaultSpecificationBaseClass
exclude package.IntegrationAnnotation
optimizeRunOrder = true
filterStackTrace = false
}
report {
enabled = true
issueNamePrefix = "JLTN-"
issueUrlPrefix = "https://github.com/Confitura/jelatyna/issues"
logFileSuffix = ".log"
...
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
0.7, 1.0
Optional runtime configuration - location
Reads from SpockConfig.groovytaken from:
-Dspock.configuration
Classpath
Spock home directory
-Dspock.user.home
$SPOCK_USER_HOME
~/.spock/
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
0.7, 1.0
Exception util
Makes assertions on nested exception causes easier
Taken after Grails
spock.util.Exceptions
Throwable getRootCause(Throwable exception)
List<Throwable> getCauseChain(Throwable exception)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Exception util
Makes assertions on nested exception causes easier
Taken after Grails
spock.util.Exceptions
Throwable getRootCause(Throwable exception)
List<Throwable> getCauseChain(Throwable exception)
def "should throw business exception on communication error keeping original"()
when:
sut.sendPing(TEST_REQUEST_ID)
then:
def e = thrown(CommunicationException)
e.message == "Communication problem when sending request with id: 5"
Exceptions.getRootCause(e).class == ConnectException
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Exception util
Makes assertions on nested exception causes easier
Taken after Grails
spock.util.Exceptions
Throwable getRootCause(Throwable exception)
List<Throwable> getCauseChain(Throwable exception)
def "should throw business exception on communication error keeping original"()
when:
sut.sendPing(TEST_REQUEST_ID)
then:
def e = thrown(CommunicationException)
e.message == "Communication problem when sending request with id: 5"
Exceptions.getRootCause(e).class == ConnectException
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
1.0
Extensibility
Powerful extension mechanism
Many of features implemented as built-in extensions
Most of already presented
Official extensions available as separate modules
Spring, Guice, Tapestry, Unitils
(Sometimes) easier to write one's own extension than
have accepted pull request
JUnit rules (@Rule/@ClassRule) can be also used
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary (aka Why I like Spock?)
Consist and readable test code
Tests as specification by default
All Groovy magic available to help
Embedded mocking framework
Although Mockito can be used if preferred
Highly extensible
Compatible with tools supporting JUnit
Marcin Zajączkowski @SolidSoftBlog Version: 1.11p-4d
Questions?
Marcin Zajączkowski
http://blog.solidsoft.info/
@SolidSoftBlog
m.zajaczkowski@gmail.com
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Thank you
(and remember about the feedback!)
Marcin Zajączkowski
http://blog.solidsoft.info/
@SolidSoftBlog
m.zajaczkowski@gmail.com
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/

More Related Content

What's hot

Introduction to OSGi (Tokyo JUG)
Introduction to OSGi (Tokyo JUG)Introduction to OSGi (Tokyo JUG)
Introduction to OSGi (Tokyo JUG)njbartlett
 
How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5
How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5
How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5TUSHAR VARSHNEY
 
Timings of Init : Android Ramdisks for the Practical Hacker
Timings of Init : Android Ramdisks for the Practical HackerTimings of Init : Android Ramdisks for the Practical Hacker
Timings of Init : Android Ramdisks for the Practical HackerStacy Devino
 
Python concurrency: libraries overview
Python concurrency: libraries overviewPython concurrency: libraries overview
Python concurrency: libraries overviewAndrii Mishkovskyi
 
Hacking Oracle From Web Apps 1 9
Hacking Oracle From Web Apps 1 9Hacking Oracle From Web Apps 1 9
Hacking Oracle From Web Apps 1 9sumsid1234
 
Testing ansible roles with molecule
Testing ansible roles with moleculeTesting ansible roles with molecule
Testing ansible roles with moleculeWerner Dijkerman
 
Integrating tomcat with apache
Integrating tomcat with apacheIntegrating tomcat with apache
Integrating tomcat with apachegovindraj8787
 
EclipseMAT
EclipseMATEclipseMAT
EclipseMATAli Bahu
 
Jmeter memory profiling, server-side monitoring, memory and cpu monitoring
Jmeter memory profiling, server-side monitoring, memory and cpu monitoringJmeter memory profiling, server-side monitoring, memory and cpu monitoring
Jmeter memory profiling, server-side monitoring, memory and cpu monitoringPankaj Biswas
 
Apache Maven supports ALL Java JEEConf 2019
Apache Maven supports ALL Java JEEConf 2019Apache Maven supports ALL Java JEEConf 2019
Apache Maven supports ALL Java JEEConf 2019Robert Scholte
 
Writing Plugged-in Java EE Apps: Jason Lee
Writing Plugged-in Java EE Apps: Jason LeeWriting Plugged-in Java EE Apps: Jason Lee
Writing Plugged-in Java EE Apps: Jason Leejaxconf
 
Java EE 6 & GlassFish v3: Paving path for the future
Java EE 6 & GlassFish v3: Paving path for the futureJava EE 6 & GlassFish v3: Paving path for the future
Java EE 6 & GlassFish v3: Paving path for the futureArun Gupta
 
S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010
S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010
S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010Arun Gupta
 
DNUG Webcast: IBM Notes V10 Performance Boost
DNUG Webcast: IBM Notes V10 Performance BoostDNUG Webcast: IBM Notes V10 Performance Boost
DNUG Webcast: IBM Notes V10 Performance BoostChristoph Adler
 
JEE Programming - 08 Enterprise Application Deployment
JEE Programming - 08 Enterprise Application DeploymentJEE Programming - 08 Enterprise Application Deployment
JEE Programming - 08 Enterprise Application DeploymentDanairat Thanabodithammachari
 
Selenium Interview Questions & Answers
Selenium Interview Questions & AnswersSelenium Interview Questions & Answers
Selenium Interview Questions & AnswersTechcanvass
 

What's hot (20)

Introduction to OSGi (Tokyo JUG)
Introduction to OSGi (Tokyo JUG)Introduction to OSGi (Tokyo JUG)
Introduction to OSGi (Tokyo JUG)
 
How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5
How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5
How to Install JAVA 7 (JDK 7u79) on CentOS_RHEL 7_6_5
 
Timings of Init : Android Ramdisks for the Practical Hacker
Timings of Init : Android Ramdisks for the Practical HackerTimings of Init : Android Ramdisks for the Practical Hacker
Timings of Init : Android Ramdisks for the Practical Hacker
 
Python concurrency: libraries overview
Python concurrency: libraries overviewPython concurrency: libraries overview
Python concurrency: libraries overview
 
Hacking Oracle From Web Apps 1 9
Hacking Oracle From Web Apps 1 9Hacking Oracle From Web Apps 1 9
Hacking Oracle From Web Apps 1 9
 
Testing ansible roles with molecule
Testing ansible roles with moleculeTesting ansible roles with molecule
Testing ansible roles with molecule
 
Integrating tomcat with apache
Integrating tomcat with apacheIntegrating tomcat with apache
Integrating tomcat with apache
 
EclipseMAT
EclipseMATEclipseMAT
EclipseMAT
 
Jmeter memory profiling, server-side monitoring, memory and cpu monitoring
Jmeter memory profiling, server-side monitoring, memory and cpu monitoringJmeter memory profiling, server-side monitoring, memory and cpu monitoring
Jmeter memory profiling, server-side monitoring, memory and cpu monitoring
 
Maven basic concept
Maven basic conceptMaven basic concept
Maven basic concept
 
Easy install
Easy installEasy install
Easy install
 
Apache Maven supports ALL Java JEEConf 2019
Apache Maven supports ALL Java JEEConf 2019Apache Maven supports ALL Java JEEConf 2019
Apache Maven supports ALL Java JEEConf 2019
 
Writing Plugged-in Java EE Apps: Jason Lee
Writing Plugged-in Java EE Apps: Jason LeeWriting Plugged-in Java EE Apps: Jason Lee
Writing Plugged-in Java EE Apps: Jason Lee
 
Java EE 6 & GlassFish v3: Paving path for the future
Java EE 6 & GlassFish v3: Paving path for the futureJava EE 6 & GlassFish v3: Paving path for the future
Java EE 6 & GlassFish v3: Paving path for the future
 
Arquillian in a nutshell
Arquillian in a nutshellArquillian in a nutshell
Arquillian in a nutshell
 
S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010
S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010
S314168 - What's New in Enterprise Java Bean Technology @ JavaOne Brazil 2010
 
DNUG Webcast: IBM Notes V10 Performance Boost
DNUG Webcast: IBM Notes V10 Performance BoostDNUG Webcast: IBM Notes V10 Performance Boost
DNUG Webcast: IBM Notes V10 Performance Boost
 
JEE Programming - 08 Enterprise Application Deployment
JEE Programming - 08 Enterprise Application DeploymentJEE Programming - 08 Enterprise Application Deployment
JEE Programming - 08 Enterprise Application Deployment
 
Glassfish JEE Server Administration - Clustering
Glassfish JEE Server Administration - ClusteringGlassfish JEE Server Administration - Clustering
Glassfish JEE Server Administration - Clustering
 
Selenium Interview Questions & Answers
Selenium Interview Questions & AnswersSelenium Interview Questions & Answers
Selenium Interview Questions & Answers
 

Similar to 4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marcin Zajączkowski

Java 6 [Mustang] - Features and Enchantments
Java 6 [Mustang] - Features and Enchantments Java 6 [Mustang] - Features and Enchantments
Java 6 [Mustang] - Features and Enchantments Pavel Kaminsky
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleAnton Arhipov
 
Riga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous IntegrationRiga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous IntegrationNicolas Fränkel
 
Most Wanted: Future PostgreSQL Features
Most Wanted: Future PostgreSQL FeaturesMost Wanted: Future PostgreSQL Features
Most Wanted: Future PostgreSQL FeaturesPeter Eisentraut
 
What's New in Spring 3.0
What's New in Spring 3.0What's New in Spring 3.0
What's New in Spring 3.0Sam Brannen
 
[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...
[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...
[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...탑크리에듀(구로디지털단지역3번출구 2분거리)
 
Plugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGemsPlugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGemsSadayuki Furuhashi
 
Declaring Server App Components in Pure Java
Declaring Server App Components in Pure JavaDeclaring Server App Components in Pure Java
Declaring Server App Components in Pure JavaAtlassian
 
Kirill Rozin - Practical Wars for Automatization
Kirill Rozin - Practical Wars for AutomatizationKirill Rozin - Practical Wars for Automatization
Kirill Rozin - Practical Wars for AutomatizationSergey Arkhipov
 
Jdk Tools For Performance Diagnostics
Jdk Tools For Performance DiagnosticsJdk Tools For Performance Diagnostics
Jdk Tools For Performance DiagnosticsDror Bereznitsky
 
Apache Maven supports all Java (JokerConf 2018)
Apache Maven supports all Java (JokerConf 2018)Apache Maven supports all Java (JokerConf 2018)
Apache Maven supports all Java (JokerConf 2018)Robert Scholte
 
Java 9 and the impact on Maven Projects (JavaOne 2016)
Java 9 and the impact on Maven Projects (JavaOne 2016)Java 9 and the impact on Maven Projects (JavaOne 2016)
Java 9 and the impact on Maven Projects (JavaOne 2016)Robert Scholte
 
Java EE 6 & GlassFish = Less Code + More Power at CEJUG
Java EE 6 & GlassFish = Less Code + More Power at CEJUGJava EE 6 & GlassFish = Less Code + More Power at CEJUG
Java EE 6 & GlassFish = Less Code + More Power at CEJUGArun Gupta
 
CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...
CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...
CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...Jesse Gallagher
 

Similar to 4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marcin Zajączkowski (20)

Java 6 [Mustang] - Features and Enchantments
Java 6 [Mustang] - Features and Enchantments Java 6 [Mustang] - Features and Enchantments
Java 6 [Mustang] - Features and Enchantments
 
Spock
SpockSpock
Spock
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 
GlassFish and JavaEE, Today and Future
GlassFish and JavaEE, Today and FutureGlassFish and JavaEE, Today and Future
GlassFish and JavaEE, Today and Future
 
Riga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous IntegrationRiga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous Integration
 
Most Wanted: Future PostgreSQL Features
Most Wanted: Future PostgreSQL FeaturesMost Wanted: Future PostgreSQL Features
Most Wanted: Future PostgreSQL Features
 
Lo nuevo en Spring 3.0
Lo nuevo  en Spring 3.0Lo nuevo  en Spring 3.0
Lo nuevo en Spring 3.0
 
What's New in Spring 3.0
What's New in Spring 3.0What's New in Spring 3.0
What's New in Spring 3.0
 
Junit_.pptx
Junit_.pptxJunit_.pptx
Junit_.pptx
 
[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...
[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...
[스프링/Spring교육학원,자바교육,근로자교육,실업자교육추천학원_탑크리에듀]#6.스프링프레임워크 & 마이바티스 (Spring Framew...
 
Plugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGemsPlugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGems
 
Declaring Server App Components in Pure Java
Declaring Server App Components in Pure JavaDeclaring Server App Components in Pure Java
Declaring Server App Components in Pure Java
 
Kirill Rozin - Practical Wars for Automatization
Kirill Rozin - Practical Wars for AutomatizationKirill Rozin - Practical Wars for Automatization
Kirill Rozin - Practical Wars for Automatization
 
Java9
Java9Java9
Java9
 
Jdk Tools For Performance Diagnostics
Jdk Tools For Performance DiagnosticsJdk Tools For Performance Diagnostics
Jdk Tools For Performance Diagnostics
 
Apache Maven supports all Java (JokerConf 2018)
Apache Maven supports all Java (JokerConf 2018)Apache Maven supports all Java (JokerConf 2018)
Apache Maven supports all Java (JokerConf 2018)
 
Spring
SpringSpring
Spring
 
Java 9 and the impact on Maven Projects (JavaOne 2016)
Java 9 and the impact on Maven Projects (JavaOne 2016)Java 9 and the impact on Maven Projects (JavaOne 2016)
Java 9 and the impact on Maven Projects (JavaOne 2016)
 
Java EE 6 & GlassFish = Less Code + More Power at CEJUG
Java EE 6 & GlassFish = Less Code + More Power at CEJUGJava EE 6 & GlassFish = Less Code + More Power at CEJUG
Java EE 6 & GlassFish = Less Code + More Power at CEJUG
 
CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...
CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...
CollabSphere 2021 - DEV114 - The Nuts and Bolts of CI/CD With a Large XPages ...
 

Recently uploaded

Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsMehedi Hasan Shohan
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 

Recently uploaded (20)

Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software Solutions
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 

4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marcin Zajączkowski

  • 1. Smarter testing Java code with Spock Framework Including changes in Spock 1.0 Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ Warszawa, 20th April 2015
  • 2. About me Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 3. 2 interesting companies Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 4. Agenda Why I like Spock? Less know, but cool Spock features New and noteworthy in Spock 1.0 Spock tricks Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 5. Why idea for this presentation? JUnit/TestNG + Mockito + AssertJ work... ... but Spock brings testing to another level Integrates seamlessly with production Java code Since version 1.0 provides even more useful features Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 6. Why idea for this presentation? JUnit/TestNG + Mockito + AssertJ work... ... but Spock brings testing to another level Integrates seamlessly with production Java code Since version 1.0 provides even more useful features Encourage more people to using Spock In even more effective way Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 7. Why I like Spock? Consist and readable test code Tests as specification by default All Groovy magic available to help Embedded mocking framework Although Mockito can be used if preferred Highly extensible Compatible with tools supporting JUnit Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 8. Simple specification in Spock given/when/then (or expect) are required to compile code class SimpleCalculatorSpec extends Specification { def "should add two numbers"() { given: def sut = new Calculator() when: def result = sut.add(1, 2) then: result == 3 } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 9. Conditional test exection Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 10. @Requires Runs annotated test (or the whole specification) only if given criteria are met Useful to depend on JVM version, operating system, activated profile, custom system property, etc. Opposite to @IgnoreIf Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 11. @Requires Runs annotated test (or the whole specification) only if given criteria are met Useful to depend on JVM version, operating system, activated profile, custom system property, etc. Opposite to @IgnoreIf Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 12. @Requires - continued Provides access to system properties and environment variables @Requires({ System.getProperty("os.arch") == "amd64" }) def "should use optimization on 64-bit systems"() { ... } @Requires({ env.containsKey("ENABLE_CRM_INTEGRATION_TESTS") }) def "should do complicated things with CRM"() { ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 13. @Requires - continued Can use static methods import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable @Requires({ isUrlAvailable("http://some-host.intranet/") }) class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification { //could be also from trait or other class static boolean isUrlAvailable(String url) { //... } def "should do one thing in intranet"() { ... } def "should do another thing in intranet"() { ... } ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 14. @Requires - continued Can use static methods import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable @Requires({ isUrlAvailable("http://some-host.intranet/") }) class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification { //could be also from trait or other class static boolean isUrlAvailable(String url) { //... } def "should do one thing in intranet"() { ... } def "should do another thing in intranet"() { ... } ... } Import is required to prevent MissingMethodException Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 15. @Requires - continued Can use static methods import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable @Requires({ isUrlAvailable("http://some-host.intranet/") }) class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification { @Memoized //could be also from trait or other class static boolean isUrlAvailable(String url) { //... } def "should do one thing in intranet"() { ... } def "should do another thing in intranet"() { ... } ... } @Memoizedfor caching Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 16. @IgnoreIf and @Requires - new features spock.util.environment.Jvm Simple and convenient access to Java version (java.versionand java.specification.version) boolean isJava7() //true only if Java 7 boolean isJava8Compatible() //true if Java 8+ String getJavaVersion() //e.g. "1.8.0_05" String getJavaSpecificationVersion() //e.g. "1.8" @IgnoreIf({ !jvm.java8Compatible }) def "should find at runtime and use CompletableFuture for Java 8+"() { ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 17. @IgnoreIf and @Requires - new features spock.util.environment.Jvm Simple and convenient access to Java version (java.versionand java.specification.version) boolean isJava7() //true only if Java 7 boolean isJava8Compatible() //true if Java 8+ String getJavaVersion() //e.g. "1.8.0_05" String getJavaSpecificationVersion() //e.g. "1.8" @IgnoreIf({ !jvm.java8Compatible }) def "should find at runtime and use CompletableFuture for Java 8+"() { ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 18. @IgnoreIf and @Requires - new features spock.util.environment.OperatingSystem Simple and convenient access to OS information (os.nameand os.versionsystem properties) String getName() //Linux String getVersion() //8.1 Family getFamily() //SOLARIS (enum) boolean isLinux() boolean isWindows() boolean isMacOs() //also Solaris and Other boolean iSolaris() boolean isOther() @Requires({ os.linux || os.macOs }) def "should use fancy console features"() { ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 19. @IgnoreIf and @Requires - new features spock.util.environment.OperatingSystem Simple and convenient access to OS information (os.nameand os.versionsystem properties) String getName() //Linux String getVersion() //8.1 Family getFamily() //SOLARIS (enum) boolean isLinux() boolean isWindows() boolean isMacOs() //also Solaris and Other boolean iSolaris() boolean isOther() @Requires({ os.linux || os.macOs }) def "should use fancy console features"() { ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 20. Cleaning up Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 21. @RestoreSystemProperties Restores original system properties From System.getProperties() To a state before test Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 22. @RestoreSystemProperties - example @Stepwise class RestoreSystemPropertiesSpec extends Specification { @RestoreSystemProperties def "should deny perform action when running as root"() { given: System.setProperty("user.name", "root") and: def sut = new DangerousActionPerformer() when: sut.squashThemAll() then: thrown(IllegalStateException) } def "should perform action when running as 'normal' user"() { given: def sut = new DangerousActionPerformer() when: sut.squashThemAll() then: noExceptionThrown() } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 23. @RestoreSystemProperties Restores original system properties From System.getProperties() To a state before test Useful in integration tests when playing with them E.g. testing Spring profiles activation based on system property Two modes For one feature (test) For the whole specification Just like using for every test Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 24. @RestoreSystemProperties Restores original system properties From System.getProperties() To a state before test Useful in integration tests when playing with them E.g. testing Spring profiles activation based on system property Two modes For one feature (test) For the whole specification Just like using for every test Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 25. @AutoCleanup Cleans up resource at the end of its life time Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 26. @AutoCleanup - with separate cleanup class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection = new H2DBConnection(...) def cleanupSpec() { dbConnection.release() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void release(); } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 27. @AutoCleanup - example class FancyDBSpec extends Specification { @AutoCleanup("release") @Shared private DBConnection dbConnection = new H2DBConnection(...) def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void release(); } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 28. @AutoCleanup Cleans up resource at the end of its life time Applied on field No need to use separate cleaup/cleanupSpecmethod Supports both instance (per feature) and shared (per specification) variables By default method close()is called Can be overridden with @AutoCleanup("release") @AutoCleanup(quiet = true)to suppress exception logging Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 29. @AutoCleanup Cleans up resource at the end of its life time Applied on field No need to use separate cleaup/cleanupSpecmethod Supports both instance (per feature) and shared (per specification) variables By default method close()is called Can be overridden with @AutoCleanup("release") @AutoCleanup(quiet = true)to suppress exception logging Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 0.7
  • 30. Initialization and clean up in test setup/cleanupcan be declared per feature useful if feature specific operations Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 31. Initialization and clean up in test setup/cleanupcan be declared per feature useful if feature specific operations def "complex logic should work with fixed thread pool"() { setup: //alias for 'given:' ExecutorService threadPool = Executors.newFixedThreadPool(1) when: String returnedValue = threadPool .submit({ "val" } as Callable) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 32. Initialization and clean up in test setup/cleanupcan be declared per feature useful if feature specific operations setupalias for given cleanuplike finallyexecuted even on exception works for every iteration (where) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 33. Native support for JUnit before/after annotations By default setupSpec(), setup(), cleanup(), cleanupSpec() In addition @BeforeClass, @Before, @After, @AfterClass Can be mixed together (also through class hierarchy) No longer called twice in super class More than one method of given type can be provided Useful when dealing with Additional testing frameworks like in Grails Traits with setup/clean up code Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 34. Simplifying specifications with traits Share common logic across different specifications No specification inheritance involved Can provide setup/cleanup methods Can have instance variables Groovy 2.3+ is required Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ G roovy2.3
  • 35. Simplifying specifications with traits trait DatabaseTrait { @Shared DBConnection dbConnection def setupSpec() { //or @BeforeClass //prepare DB } def cleanupSpec() { //or @AfterClass //clean up DB } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 36. Simplifying specifications with traits trait DatabaseTrait { @Shared DBConnection dbConnection def setupSpec() { //or @BeforeClass //prepare DB } def cleanupSpec() { //or @AfterClass //clean up DB } } class Fancy1DatabaseSpec extends Specification implements DatabaseTrait { def "should do fancy1 thing on database"() { //fancy1 things on database } } class Fancy2DatabaseSpec extends Specification implements DatabaseTrait { def "should do fancy2 thing on database"() { //fancy2 things on database } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 37. Parametrized tests Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 38. Parametrized tests - basic case def "should add two integers"() { given: Calculator sut = new SimpleCalculator() when: int result = sut.add(x, y) then: result == expectedResult where: x | y || expectedResult 1 | 2 || 3 -2 | 3 || 1 -1 | -2 || -3 } build-in support with wherekeyword does not stop on failure for given test case syntatic sugar for table-like data formatting Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 39. Parametrized tests - could look better private PESELValidator sut = new FakePESELValidator() def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 40. Parametrized tests - could look better private PESELValidator sut = new FakePESELValidator() def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 41. Parametrized tests - could look better private PESELValidator sut = new FakePESELValidator() def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } one combined test for all test cases not very readable in case of failure(s) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 42. Parametrized tests - @Unroll @Unroll def "should validate PESEL correctness (multiple tests)"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 43. Parametrized tests - @Unroll @Unroll def "should validate PESEL correctness (multiple tests)"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 44. Parametrized tests - @Unroll @Unroll def "should validate PESEL correctness (multiple tests)"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } @Unrollto generate separate test per test case still confusing in case of failure Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 45. Parametrized tests - @Unroll + #pesel @Unroll def "should validate PESEL (#pesel) correctness (#isValid)"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 46. Parametrized tests - @Unroll + #pesel @Unroll def "should validate PESEL (#pesel) correctness (#isValid)"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 47. Parametrized tests - @Unroll + #pesel @Unroll def "should validate PESEL (#pesel) correctness (#isValid)"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } #peseland #isValidto put input parameter into test report more readable, but still can be confusing for business people reading BDD reports Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 48. Parametrized tests - @Unroll with message @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true description = isValid ? "valid" : "invalid" } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 49. Parametrized tests - @Unroll with message @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true description = isValid ? "valid" : "invalid" } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 50. Parametrized tests - @Unroll with message @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true description = isValid ? "valid" : "invalid" } distinguish test name and test description used in test report additional (artificial) parameter for test report only with autocompletion in IntelliJ IDEA Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 51. Parametrized tests - data pipes @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"() { expect: sut.validate(pesel) == isValid where: pesel << ["123", "abcd", "97110208631"] isValid << [false, false, true] description = isValid ? "valid" : "invalid" } table is syntatic sugar for data pipes "<<" operator to connect data variable with data provider Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 52. Parametrized tests - more data providers @Unroll("#pesel is valid (#dbId)") def "should validate PESEL correctness (CSV)"() { expect: sut.validate(pesel) where: [dbId, _, _, pesel] << readValidPeopleFromCSVFile() .readLines().collect { it.split(',') } //ugly way to read CSV - don't do this } not only static elements everything iterable in Groovy also SQL rows or CSV files Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 53. Mocking Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 54. Simple Stubbing class DaoSpec extends Specification { def "should stub method call"() { given: Dao dao = Stub(Dao) dao.getCount() >> 1 expect: dao.getCount() == 1 } } interface Dao { int getCount() Item save(Item item) } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 55. Simple Stubbing - inline class DaoSpec extends Specification { private Dao<Item> dao = Stub(Dao) { dao.getCount() >> 1 } def "should stub method call"() { expect: dao.getCount() == 1 } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 56. Simple Stubbing - consecutive calls class DaoSpec extends Specification { def "should stub consecutive calls"() { given: Dao<Item> dao = Stub(Dao) dao.getCount() >> 1 >> 2 expect: dao.getCount() == 1 and: dao.getCount() == 2 and: dao.getCount() == 2 } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 57. Simple Stubbing - consecutive calls (list) class DaoSpec extends Specification { def "should stub consecutive calls (list)"() { given: Dao<Item> dao = Stub(Dao) dao.getCount() >>> [1, 2] expect: dao.getCount() == 1 and: dao.getCount() == 2 and: dao.getCount() == 2 } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 58. Simple Stubbing - input parameters (array) class DaoSpec extends Specification { def "should stub method call to return input value"() { given: Item baseItem = new Item() and: Dao<Item> dao = Stub(Dao) dao.save(_) >> { it[0] } when: Item returnedItem = dao.save(baseItem) then: baseItem.is(returnedItem) } } by default array of input parameters Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 59. Simple Stubbing - named input parameters class DaoSpec extends Specification { def "should stub method call to return input value"() { given: Item baseItem = new Item() and: Dao<Item> dao = Stub(Dao) dao.save(_) >> { Item item -> item } when: Item returnedItem = dao.save(baseItem) then: baseItem.is(returnedItem) } } can be declared explicit as named (and typed) parameters all have to be declared Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 60. Simple Stubbing - throwing exception class DaoSpec extends Specification { def "should throw exception for specific input parameters"() { given: Dao<Item> dao = Stub(Dao) dao.save(_) >> { Item item -> throw new IllegalArgumentException(item.toString()) } when: dao.save(new Item()) then: thrown(IllegalArgumentException) } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 61. Simple Stubbing - throwing exception class DaoSpec extends Specification { def "should throw exception for specific input parameters"() { given: Dao<Item> dao = Stub(Dao) dao.save(_) >> { Item item -> throw new IllegalArgumentException(item.toString()) } when: dao.save(new Item()) then: thrown(IllegalArgumentException) when: dao.save(null) then: thrown(IllegalArgumentException) } } smart toString()from Groovy (for null objects) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 62. Stubbing and verifying together class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao<Item> dao = Stub(Dao) when: Item returnedItem = dao.save(baseItem) then: 1 * dao.save(_) >> { Item item -> item } and: baseItem.is(returnedItem) } } has to be in the same statement in thensection Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 63. Overriding stubbed value class BusinessServiceSpec extends Specification { private BoringCollaborator boringStub = Stub(BoringCollaborator) { sendPing() >> "OK" } private BusinessService sut = new BusinessService(boringStub) def "should calculate important business case 1"() { when: int result = sut.calculateImportantThings() then: result == EXPECTED_RESULT_1 } def "should calculate important business case 2"() { ... } many positive cases with sendPing() >> "OK" how to test negative scenario? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 64. Overriding stubbed value - broken class BusinessServiceSpec extends Specification { private BoringCollaborator boringStub = Stub(BoringCollaborator) { sendPing() >> "OK" } private BusinessService sut = new BusinessService(boringStub) def "should calculate important business case 1"() { ... } def "should calculate important business case 2"() { ... } @Ignore("broken") def "should cover collaborator error"() { given: boringStub.sendPing() >> "ERROR" //does not work! when: sut.calculateImportantThings() then: thrown(IllegalStateException) } override in Mockito style does not work Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 65. Overriding stubbed value - in thensection class BusinessServiceSpec extends Specification { private BoringCollaborator boringStub = Stub(BoringCollaborator) { sendPing() >> "OK" } private BusinessService sut = new BusinessService(boringStub) def "should calculate important business case 1"() { ... } def "should calculate important business case 2"() { ... } def "should cover collaborator error"() { when: sut.calculateImportantThings() then: boringStub.sendPing() >> "ERROR" //stubbing in "then", looks bad and: thrown(IllegalStateException) } has to placed in thensection Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 66. Verification in order - issue @Ignore("broken") def "notification should be send after business operation (ignore order)"() { given: Dao dao = Mock() Notifier notifier = Mock() and: def sut = new BusinessService(dao, notifier) when: sut.processOrder() then: 1 * dao.saveOrder(_) //order does not matter 1 * notifier.sendNotification(_) } order is not verified notification could be send before saving order Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 67. Verification in order - solution def "notification should be send after business operation"() { given: Dao dao = Mock() Notifier notifier = Mock() and: def sut = new BusinessService(dao, notifier) when: sut.processOrder() then: 1 * dao.saveOrder(_) then: 1 * notifier.sendNotification(_) } mutiple then block are evaluated separately Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 68. Verification in order - gotcha @Ignore("broken") def "notification should be send after business operation (with 'and')"() { given: Dao dao = Mock() Notifier notifier = Mock() and: def sut = new BusinessService(dao, notifier) when: sut.processOrder() then: 1 * dao.saveOrder(_) and: //'and' does not verify execution order 1 * notifier.sendNotification(_) } anddoes not verify execution order it is for documentation purposes only Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 70. Active condition waiting PoolingConditions Active waiting for condition to pass - like in Awaitility No more sleep(3000) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 71. Active condition waiting - example def "should receive packet after while"() { given: def conditions = new PollingConditions() def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade() when: asynchronousMessageQueue.sendPing() then: conditions.eventually { assert asynchronousMessageQueue.numberOfReceivedPackets == 1 } } With default values Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 72. Active condition waiting - example def "should receive packet after while"() { given: def conditions = new PollingConditions(timeout: 2, initialDelay: 0.3) def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade() when: asynchronousMessageQueue.sendPing() then: conditions.eventually { assert asynchronousMessageQueue.numberOfReceivedPackets == 1 } } With custom configuration Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 73. Active condition waiting - example def "should receive packet after while"() { given: def conditions = new PollingConditions(timeout: 2, initialDelay: 0.3) def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade() when: asynchronousMessageQueue.sendPing() then: conditions.within(5) { assert asynchronousMessageQueue.numberOfReceivedPackets == 1 } } withing(timeoutInSeconds)can override original timeout in specific call Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 74. Active condition waiting - example def "should receive packet after while"() { given: PollingConditions conditions = new PollingConditions() def asynchronousMessageQueue = new DelayedDeliveryMessageQueueFacade() when: asynchronousMessageQueue.sendPing() then: conditions.eventually { asynchronousMessageQueue.numberOfReceivedPackets == 1 } } assertkeyword can omitted when PollingConditionsis declared explicit (not with def) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 75. Active condition waiting PoolingConditions Active waiting for condition to pass - like in Awaitility No more sleep(3000) Ability to specify: timeout, initialDelay, delay and factor assertkeyword is (usually) required Useful in multithreading code and asynchronous external calls (like message queues) Cannot be used for interaction verification Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 76. Active condition waiting PoolingConditions Active waiting for condition to pass - like in Awaitility No more sleep(3000) Ability to specify: timeout, initialDelay, delay and factor assertkeyword is (usually) required Useful in multithreading code and asynchronous external calls (like message queues) Cannot be used for interaction verification Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 0.7
  • 77. Asserting previous value (manual way) Check if/how given value was changed def "should create new record when item is saved (manual way)"() { given: Dao dao = new DummyDao() and: int originalNumberOfRecords = dao.getCount() when: dao.save(new Item()) then: dao.getCount() == originalNumberOfRecords + 1 } interface Dao { int getCount() void save(Item item) } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 78. Asserting previous value (Spock way) def "should create new record when item is saved"() { given: Dao dao = new DummyDao() when: dao.save(new Item()) then: dao.getCount() == old(dao.getCount()) + 1 } interface Dao { int getCount() void save(Item item) } Spock generates code to keep the old value Works with fields and methods Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 79. BDD-like documentation Highlight business context Block description (when: "put element on stack") @See("https://en.wikipedia.org/wiki/Spock") @Issue("http://tinyurl.com/spock-issue260") @Subject("TODO: przyklady") @Title @Narrative Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 80. Optional runtime configuration Configures Spock behavior Injectable into run context and extensions By default for Runner Report Ability to create custom configuration when needed Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 0.7, 1.0
  • 81. Optional runtime configuration ~/.spock/SpockConfig.groovy runner { include package.DefaultSpecificationBaseClass exclude package.IntegrationAnnotation optimizeRunOrder = true filterStackTrace = false } report { enabled = true issueNamePrefix = "JLTN-" issueUrlPrefix = "https://github.com/Confitura/jelatyna/issues" logFileSuffix = ".log" ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 0.7, 1.0
  • 82. Optional runtime configuration - location Reads from SpockConfig.groovytaken from: -Dspock.configuration Classpath Spock home directory -Dspock.user.home $SPOCK_USER_HOME ~/.spock/ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 0.7, 1.0
  • 83. Exception util Makes assertions on nested exception causes easier Taken after Grails spock.util.Exceptions Throwable getRootCause(Throwable exception) List<Throwable> getCauseChain(Throwable exception) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 84. Exception util Makes assertions on nested exception causes easier Taken after Grails spock.util.Exceptions Throwable getRootCause(Throwable exception) List<Throwable> getCauseChain(Throwable exception) def "should throw business exception on communication error keeping original"() when: sut.sendPing(TEST_REQUEST_ID) then: def e = thrown(CommunicationException) e.message == "Communication problem when sending request with id: 5" Exceptions.getRootCause(e).class == ConnectException } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 85. Exception util Makes assertions on nested exception causes easier Taken after Grails spock.util.Exceptions Throwable getRootCause(Throwable exception) List<Throwable> getCauseChain(Throwable exception) def "should throw business exception on communication error keeping original"() when: sut.sendPing(TEST_REQUEST_ID) then: def e = thrown(CommunicationException) e.message == "Communication problem when sending request with id: 5" Exceptions.getRootCause(e).class == ConnectException } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 86. Extensibility Powerful extension mechanism Many of features implemented as built-in extensions Most of already presented Official extensions available as separate modules Spring, Guice, Tapestry, Unitils (Sometimes) easier to write one's own extension than have accepted pull request JUnit rules (@Rule/@ClassRule) can be also used Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 87. Summary (aka Why I like Spock?) Consist and readable test code Tests as specification by default All Groovy magic available to help Embedded mocking framework Although Mockito can be used if preferred Highly extensible Compatible with tools supporting JUnit Marcin Zajączkowski @SolidSoftBlog Version: 1.11p-4d
  • 89. Thank you (and remember about the feedback!) Marcin Zajączkowski http://blog.solidsoft.info/ @SolidSoftBlog m.zajaczkowski@gmail.com Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/