SlideShare a Scribd company logo
1 of 111
Download to read offline
Smarter Java code testing
with Spock Framework
Advanced features
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Kraków, 12-13th October 2015
About me
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
2 interesting companies
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Agenda
Less know, but cool Spock features
Including chances in Spock 1.0
Spock tricks & traps
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 start using Spock
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 start using Spock
In even more effective way
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simple specification in Spock
class SimpleCalculatorSpec extends Specification {
def "should add two numbers"() {
given:
def sut = new Calculator()
when:
def result = sut.add(1, 2)
then:
result == 3
}
}
[given]/when/then (or expect) are required to compile code
not just comments in code
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Conditional test execution
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 - 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/
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()
boolean isSolaris()
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/
1.0
Manual cleaning up - very verbose
class FancyDBSpec extends Specification {
@Shared
private DBConnection dbConnection
def beforeSpec() {
dbConnection = new H2DBConnection(...)
}
def cleanupSpec() {
dbConnection.close()
}
def "should do fancy things on DB"() { ... }
def "should do fancy2 things on DB"() { ... }
}
interface DBConnection {
...
void close()
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Manual cleaning up - inline initialization
class FancyDBSpec extends Specification {
@Shared
private DBConnection dbConnection = new H2DBConnection(...)
def cleanupSpec() {
dbConnection.close()
}
def "should do fancy things on DB"() { ... }
def "should do fancy2 things on DB"() { ... }
}
interface DBConnection {
...
void close();
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Manual cleaning up - inline initialization
class FancyDBSpec extends Specification {
@Shared
private DBConnection dbConnection = new H2DBConnection(...)
def cleanupSpec() {
dbConnection.close()
}
def "should do fancy things on DB"() { ... }
def "should do fancy2 things on DB"() { ... }
}
interface DBConnection {
...
void close();
}
Woudn't be nice to have inline clean up?
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@AutoCleanup - inline clean up
class FancyDBSpec extends Specification {
@Shared
@AutoCleanup
private DBConnection dbConnection = new H2DBConnection(...)
def "should do fancy things on DB"() { ... }
def "should do fancy2 things on DB"() { ... }
}
interface DBConnection {
...
void close();
}
By default close()method is called
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
@AutoCleanup - non default method name
class FancyDBSpec extends Specification {
@Shared
@AutoCleanup("release")
private DBConnection dbConnection = new H2DBConnection(...)
def "should do fancy things on DB"() { ... }
def "should do fancy2 things on DB"() { ... }
}
interface DBConnection {
...
void release();
}
Method name can be overridden
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
def "complex logic should work with fixed thread pool"() {
setup:
ExecutorService threadPool = Executors.newFixedThreadPool(1)
when:
String returnedValue = threadPool
.submit({ "val" } as Callable)
.get(1, TimeUnit.SECONDS)
then:
returnedValue == "val"
cleanup:
threadPool?.shutdown()
}
Can task code be extracted?
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Initialization and clean up in test
def "complex logic should work with fixed thread pool"() {
setup:
ExecutorService threadPool = Executors.newFixedThreadPool(1)
and:
Callable task = { "val" } as Callable
when:
String returnedValue = threadPool
.submit(task)
.get(1, TimeUnit.SECONDS)
then:
returnedValue == "val"
cleanup:
threadPool?.shutdown()
}
Will it work?
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Initialization and clean up in test
def "complex logic should work with fixed thread pool"() {
setup:
ExecutorService threadPool = Executors.newFixedThreadPool(1)
and:
Callable task = { "val" } as Callable
when:
String returnedValue = threadPool
.submit(task)
.get(1, TimeUnit.SECONDS) //null is returned!
then:
returnedValue == "val"
cleanup:
threadPool?.shutdown()
}
Gotcha: reference to "{ 'val' } as Callable" (instead of
inlined expression) doesn't work as expected if method
takes both Runnable and Callable arguments
Groovy implementation nuance
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Initialization and clean up in test
def "complex logic should work with fixed thread pool"() {
setup:
ExecutorService threadPool = Executors.newFixedThreadPool(1)
and:
Callable task = [call: { "val" }] as Callable
when:
String returnedValue = threadPool
.submit(task)
.get(1, TimeUnit.SECONDS)
then:
returnedValue == "val"
cleanup:
threadPool?.shutdown()
}
Map coerced to Callable as a workaround
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
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+
| BaseIntegrationSpec |
+---------------------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+
| BaseIntegrationSpec |
+---------------------+
^
|
+------------------+
| BaseDatabaseSpec |
+------------------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+
| BaseIntegrationSpec |
+---------------------+
^
|
+------------------+
| BaseDatabaseSpec |
+------------------+
^ ^
| |
| |
+-------- + +---------+
| DbSpec1 | | DbSpec2 |
+---------+ +---------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+
| BaseIntegrationSpec |
+---------------------+
^ ^
| |
+------------------+ +----------------- +
| BaseDatabaseSpec | | BaseWiremockSpec |
+------------------+ +------------------+
^ ^ ^ ^
| | | |
| | | |
+-------- + +---------+ +---------------+ +---------------+
| DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 |
+---------+ +---------+ +---------------+ +---------------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+ +--------------------------- +
| BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? |
+---------------------+ +----------------------------+
^ ^
| |
+------------------+ +----------------- +
| BaseDatabaseSpec | | BaseWiremockSpec |
+------------------+ +------------------+
^ ^ ^ ^
| | | |
| | | |
+-------- + +---------+ +---------------+ +---------------+
| DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 |
+---------+ +---------+ +---------------+ +---------------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+ +--------------------------- +
| BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? |
+---------------------+ +----------------------------------+
^ ^ | BaseWiremockWithActiveMqSpec ??? |
| | +----------------------------------+
+------------------+ +----------------- +
| BaseDatabaseSpec | | BaseWiremockSpec |
+------------------+ +------------------+
^ ^ ^ ^
| | | |
| | | |
+-------- + +---------+ +---------------+ +---------------+
| DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 |
+---------+ +---------+ +---------------+ +---------------+
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Integration tests (specifications) hierarchy
+---------------+
| Specification |
+---------------+
^
|
+---------------------+ +--------------------------- +
| BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? |
+---------------------+ +----------------------------------+
^ ^ | BaseWiremockWithActiveMqSpec ??? |
| | +----------------------------------+
+------------------+ +----------------- +
| BaseDatabaseSpec | | BaseWiremockSpec |
+------------------+ +------------------+
^ ^ ^ ^
| | | |
| | | |
+-------- + +---------+ +---------------+ +---------------+
| DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 |
+---------+ +---------+ +---------------+ +---------------+
Class hierarchy does not scale that way
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simplifying specifications with traits
Share common logic across different specifications
No specification inheritance involved
No problem with multiple traits per class
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 {
static DBConnection dbConnection
def setupSpec() { //or @BeforeClass or inlined
//prepare DB
}
def cleanupSpec() { //or @AfterClass
//clean up DB
}
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Simplifying specifications with traits
trait DatabaseTrait {
static DBConnection dbConnection
def setupSpec() { //or @BeforeClass or inlined
//prepare DB
}
def cleanupSpec() { //or @AfterClass
//clean up DB
}
}
//no more infrastructure code needed in specifications
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/
Simplifying specifications with traits -
gotchas
issue with obtaining annotations on fields
some field-based extensions are not available
@Sharedhas to be replaced with static
@AutoCleanuphas to be replaced with cleanup(Spec)
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parameterized tests
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parameterized 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
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized 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/
Parameterized tests - @Unroll w/ 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/
Parameterized tests - @Unroll w/ 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/
Parameterized tests - @Unroll w/ 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/
Parameterized 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/
Parameterized tests - 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/
Parameterized tests - @Unrollfor all
unfortunately @Unrollis not enabled by default
for simple cases can be put as a class level
no side effects for not parameterized tests
@Unroll
class PeselValidatorSpec extends Specification {
def "should pass valid PESEL numbers"() { ... }
def "should reject too short PESEL numbers"() { ... }
def "should reject wrong checksum PESEL numbers"() { ... }
(...)
}
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
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(_) >> { ??? }
when:
Item returnedItem = dao.save(baseItem)
then:
baseItem.is(returnedItem)
}
}
How to just return return the first method execution
argument?
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
How to achieve it in code?
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 = Mock(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"() { ... }
def "should calculate important business case 3"() { ... }
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
Condition is checked multiple times per second
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
PoolingConditions
Active waiting for condition to pass - like in Awaitility
Condition is checked multiple times per second
No more sleep(3000)
Ability to specify: timeout, initialDelay, delay and factor
assertkeyword is (usually) required
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Active condition waiting
PoolingConditions
Active waiting for condition to pass - like in Awaitility
Condition is checked multiple times per second
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
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
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)
}
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/
Capturing thrown exception - long syntax
def "should capture exception - long form"() {
when:
throwNPE()
then:
NullPointerException e = thrown(NullPointerException)
e.message == "test NPE"
}
Capture thrown exception (and bind it to variable)
Fails the test if no exception was thrown
Fails the test if other exception was thrown
Requires redundant information
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Capturing thrown exception - short syntax
def "should capture exception - shorten 1"() {
when:
throwNPE()
then:
def e = thrown(NullPointerException)
e.message == "test NPE"
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Capturing thrown exception - short syntax
def "should capture exception - shorten 1"() {
when:
throwNPE()
then:
def e = thrown(NullPointerException)
e.message == "test NPE"
}
def "should capture exception - shorten 2"() {
when:
throwNPE()
then:
NullPointerException e = thrown()
e.message == "test NPE"
}
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Capturing thrown exception - short syntax
def "should capture exception - shorten 1"() {
when:
throwNPE()
then:
def e = thrown(NullPointerException)
e.message == "test NPE"
}
def "should capture exception - shorten 2"() {
when:
throwNPE()
then:
NullPointerException e = thrown()
e.message == "test NPE"
}
Identical behavior to the long one
Choose which you like best
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)
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
(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
Flat learning curve to start using Spock, but...
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Multiple cool features waiting to be discovered
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Multiple cool features waiting to be discovered
For everyday use
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Multiple cool features waiting to be discovered
For everyday use
For just one particular thing
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Multiple cool features waiting to be discovered
For everyday use
For just one particular thing
But done in the awesome way
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Multiple cool features waiting to be discovered
For everyday use
For just one particular thing
But done in the awesome way
Know your tools
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
Summary
Flat learning curve to start using Spock, but...
... this is just the beginning
Multiple cool features waiting to be discovered
For everyday use
For just one particular thing
But done in the awesome way
Know your tools
... and use Spock in even more effective way
Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
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

アーキテクチャから理解するPostgreSQLのレプリケーション
アーキテクチャから理解するPostgreSQLのレプリケーションアーキテクチャから理解するPostgreSQLのレプリケーション
アーキテクチャから理解するPostgreSQLのレプリケーションMasahiko Sawada
 
論文紹介: The New New Product Development Game
論文紹介: The New New Product Development Game論文紹介: The New New Product Development Game
論文紹介: The New New Product Development GameHitoshi Otsuki
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計Yoshinori Matsunobu
 
REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】
REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】
REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】洵貴 佐川
 
世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)
世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)
世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャKohei KaiGai
 
PostgreSQL 15 開発最新情報
PostgreSQL 15 開発最新情報PostgreSQL 15 開発最新情報
PostgreSQL 15 開発最新情報Masahiko Sawada
 
トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...
トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...
トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...NTT DATA Technology & Innovation
 
オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)
オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)
オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)NTT DATA Technology & Innovation
 
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
iostat await svctm の 見かた、考え方
iostat await svctm の 見かた、考え方iostat await svctm の 見かた、考え方
iostat await svctm の 見かた、考え方歩 柴田
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)NTT DATA Technology & Innovation
 
VirtualBox と Rocky Linux 8 で始める Pacemaker ~ VirtualBox でも STONITH 機能が試せる! Vi...
VirtualBox と Rocky Linux 8 で始める Pacemaker  ~ VirtualBox でも STONITH 機能が試せる! Vi...VirtualBox と Rocky Linux 8 で始める Pacemaker  ~ VirtualBox でも STONITH 機能が試せる! Vi...
VirtualBox と Rocky Linux 8 で始める Pacemaker ~ VirtualBox でも STONITH 機能が試せる! Vi...ksk_ha
 
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 

What's hot (20)

アーキテクチャから理解するPostgreSQLのレプリケーション
アーキテクチャから理解するPostgreSQLのレプリケーションアーキテクチャから理解するPostgreSQLのレプリケーション
アーキテクチャから理解するPostgreSQLのレプリケーション
 
PostgreSQL: XID周回問題に潜む別の問題
PostgreSQL: XID周回問題に潜む別の問題PostgreSQL: XID周回問題に潜む別の問題
PostgreSQL: XID周回問題に潜む別の問題
 
論文紹介: The New New Product Development Game
論文紹介: The New New Product Development Game論文紹介: The New New Product Development Game
論文紹介: The New New Product Development Game
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】
REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】
REST API、gRPC、GraphQL 触ってみた【2023年12月開催勉強会資料】
 
GitLab から GitLab に移行したときの思い出
GitLab から GitLab に移行したときの思い出GitLab から GitLab に移行したときの思い出
GitLab から GitLab に移行したときの思い出
 
世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)
世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)
世の中のPostgreSQLエンジニアのpsql設定(第34回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ
 
PostgreSQL 15 開発最新情報
PostgreSQL 15 開発最新情報PostgreSQL 15 開発最新情報
PostgreSQL 15 開発最新情報
 
トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...
トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...
トランザクション処理可能な分散DB 「YugabyteDB」入門(Open Source Conference 2022 Online/Fukuoka 発...
 
オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)
オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)
オンライン物理バックアップの排他モードと非排他モードについて(第15回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL16新機能紹介 - libpq接続ロード・バランシング(第41回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
 
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
 
使ってみませんか?pg_hint_plan
使ってみませんか?pg_hint_plan使ってみませんか?pg_hint_plan
使ってみませんか?pg_hint_plan
 
iostat await svctm の 見かた、考え方
iostat await svctm の 見かた、考え方iostat await svctm の 見かた、考え方
iostat await svctm の 見かた、考え方
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
 
VirtualBox と Rocky Linux 8 で始める Pacemaker ~ VirtualBox でも STONITH 機能が試せる! Vi...
VirtualBox と Rocky Linux 8 で始める Pacemaker  ~ VirtualBox でも STONITH 機能が試せる! Vi...VirtualBox と Rocky Linux 8 で始める Pacemaker  ~ VirtualBox でも STONITH 機能が試せる! Vi...
VirtualBox と Rocky Linux 8 で始める Pacemaker ~ VirtualBox でも STONITH 機能が試せる! Vi...
 
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
 

Similar to Sprytniejsze testowanie kodu java ze spock framework (zaawansowane techniki) - Marcin Zajączkowski

4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...
4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...
4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...PROIDEA
 
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
 
Arquillian Constellation
Arquillian ConstellationArquillian Constellation
Arquillian ConstellationAlex Soto
 
«Продакшн в Kotlin DSL» Сергей Рыбалкин
«Продакшн в Kotlin DSL» Сергей Рыбалкин«Продакшн в Kotlin DSL» Сергей Рыбалкин
«Продакшн в Kotlin DSL» Сергей РыбалкинMail.ru Group
 
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
 
Session 24 - JDBC, Intro to Enterprise Java
Session 24 - JDBC, Intro to Enterprise JavaSession 24 - JDBC, Intro to Enterprise Java
Session 24 - JDBC, Intro to Enterprise JavaPawanMM
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!DataArt
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
A brief overview of java frameworks
A brief overview of java frameworksA brief overview of java frameworks
A brief overview of java frameworksMD Sayem Ahmed
 
Entity Framework: Nakov @ BFU Hackhaton 2015
Entity Framework: Nakov @ BFU Hackhaton 2015Entity Framework: Nakov @ BFU Hackhaton 2015
Entity Framework: Nakov @ BFU Hackhaton 2015Svetlin Nakov
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e bigAndy Peterson
 
Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)
Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)
Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)Is Antipov
 
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
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
Ztech Connect '19, IBM PureApplication
Ztech Connect '19, IBM PureApplicationZtech Connect '19, IBM PureApplication
Ztech Connect '19, IBM PureApplicationChris Clark
 
JDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek Piotrowski
JDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek PiotrowskiJDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek Piotrowski
JDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek PiotrowskiPROIDEA
 

Similar to Sprytniejsze testowanie kodu java ze spock framework (zaawansowane techniki) - Marcin Zajączkowski (20)

4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...
4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...
4Developers 2015: Sprytniejsze testowanie kodu Java ze Spock Framework - Marc...
 
Java 6 [Mustang] - Features and Enchantments
Java 6 [Mustang] - Features and Enchantments Java 6 [Mustang] - Features and Enchantments
Java 6 [Mustang] - Features and Enchantments
 
Arquillian Constellation
Arquillian ConstellationArquillian Constellation
Arquillian Constellation
 
«Продакшн в Kotlin DSL» Сергей Рыбалкин
«Продакшн в Kotlin DSL» Сергей Рыбалкин«Продакшн в Kotlin DSL» Сергей Рыбалкин
«Продакшн в Kotlin DSL» Сергей Рыбалкин
 
JDBC Part - 2
JDBC Part - 2JDBC Part - 2
JDBC Part - 2
 
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
 
Session 24 - JDBC, Intro to Enterprise Java
Session 24 - JDBC, Intro to Enterprise JavaSession 24 - JDBC, Intro to Enterprise Java
Session 24 - JDBC, Intro to Enterprise Java
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!
 
Spock
SpockSpock
Spock
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
A brief overview of java frameworks
A brief overview of java frameworksA brief overview of java frameworks
A brief overview of java frameworks
 
Entity Framework: Nakov @ BFU Hackhaton 2015
Entity Framework: Nakov @ BFU Hackhaton 2015Entity Framework: Nakov @ BFU Hackhaton 2015
Entity Framework: Nakov @ BFU Hackhaton 2015
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e big
 
Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)
Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)
Automatic deployment on .NET web stack (Minsk .NET meetup 12.02.14)
 
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
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Group111
Group111Group111
Group111
 
Full Stack Scala
Full Stack ScalaFull Stack Scala
Full Stack Scala
 
Ztech Connect '19, IBM PureApplication
Ztech Connect '19, IBM PureApplicationZtech Connect '19, IBM PureApplication
Ztech Connect '19, IBM PureApplication
 
JDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek Piotrowski
JDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek PiotrowskiJDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek Piotrowski
JDD2015: ClassIndex - szybka alternatywa dla skanowania klas - Sławek Piotrowski
 

Recently uploaded

Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
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
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
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
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
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
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
(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
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 

Recently uploaded (20)

Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
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...
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
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
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
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...
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
(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...
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 

Sprytniejsze testowanie kodu java ze spock framework (zaawansowane techniki) - Marcin Zajączkowski

  • 1. Smarter Java code testing with Spock Framework Advanced features Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ Kraków, 12-13th October 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 Less know, but cool Spock features Including chances in Spock 1.0 Spock tricks & traps 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 start using Spock Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 7. 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 start using Spock In even more effective way Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 8. Simple specification in Spock class SimpleCalculatorSpec extends Specification { def "should add two numbers"() { given: def sut = new Calculator() when: def result = sut.add(1, 2) then: result == 3 } } [given]/when/then (or expect) are required to compile code not just comments in code Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 9. Conditional test execution 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 - 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/
  • 12. @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/
  • 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"() { ... } ... } Import is required to prevent MissingMethodException 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 { @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/
  • 15. @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
  • 16. @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() boolean isSolaris() boolean isOther() @Requires({ os.linux || os.macOs }) def "should use fancy console features"() { ... } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/ 1.0
  • 17. Cleaning up Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 18. @RestoreSystemProperties Restores original system properties From System.getProperties() To a state before test Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 19. @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/
  • 20. @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
  • 21. Manual cleaning up - very verbose class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection def beforeSpec() { dbConnection = new H2DBConnection(...) } def cleanupSpec() { dbConnection.close() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close() } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 22. Manual cleaning up - inline initialization class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection = new H2DBConnection(...) def cleanupSpec() { dbConnection.close() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close(); } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 23. Manual cleaning up - inline initialization class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection = new H2DBConnection(...) def cleanupSpec() { dbConnection.close() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close(); } Woudn't be nice to have inline clean up? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 24. @AutoCleanup - inline clean up class FancyDBSpec extends Specification { @Shared @AutoCleanup private DBConnection dbConnection = new H2DBConnection(...) def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close(); } By default close()method is called Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 25. @AutoCleanup - non default method name class FancyDBSpec extends Specification { @Shared @AutoCleanup("release") private DBConnection dbConnection = new H2DBConnection(...) def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void release(); } Method name can be overridden Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 26. @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
  • 27. 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/
  • 28. 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/
  • 29. 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: ExecutorService threadPool = Executors.newFixedThreadPool(1) when: String returnedValue = threadPool .submit({ "val" } as Callable) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() } Can task code be extracted? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 30. Initialization and clean up in test def "complex logic should work with fixed thread pool"() { setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) and: Callable task = { "val" } as Callable when: String returnedValue = threadPool .submit(task) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() } Will it work? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 31. Initialization and clean up in test def "complex logic should work with fixed thread pool"() { setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) and: Callable task = { "val" } as Callable when: String returnedValue = threadPool .submit(task) .get(1, TimeUnit.SECONDS) //null is returned! then: returnedValue == "val" cleanup: threadPool?.shutdown() } Gotcha: reference to "{ 'val' } as Callable" (instead of inlined expression) doesn't work as expected if method takes both Runnable and Callable arguments Groovy implementation nuance Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 32. Initialization and clean up in test def "complex logic should work with fixed thread pool"() { setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) and: Callable task = [call: { "val" }] as Callable when: String returnedValue = threadPool .submit(task) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() } Map coerced to Callable as a workaround Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 33. 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/
  • 34. 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
  • 35. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 36. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec | +---------------------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 37. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec | +---------------------+ ^ | +------------------+ | BaseDatabaseSpec | +------------------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 38. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec | +---------------------+ ^ | +------------------+ | BaseDatabaseSpec | +------------------+ ^ ^ | | | | +-------- + +---------+ | DbSpec1 | | DbSpec2 | +---------+ +---------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 39. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec | +---------------------+ ^ ^ | | +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 40. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ +--------------------------- + | BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? | +---------------------+ +----------------------------+ ^ ^ | | +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 41. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ +--------------------------- + | BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? | +---------------------+ +----------------------------------+ ^ ^ | BaseWiremockWithActiveMqSpec ??? | | | +----------------------------------+ +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+ Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 42. Integration tests (specifications) hierarchy +---------------+ | Specification | +---------------+ ^ | +---------------------+ +--------------------------- + | BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? | +---------------------+ +----------------------------------+ ^ ^ | BaseWiremockWithActiveMqSpec ??? | | | +----------------------------------+ +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+ Class hierarchy does not scale that way Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 43. Simplifying specifications with traits Share common logic across different specifications No specification inheritance involved No problem with multiple traits per class 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
  • 44. Simplifying specifications with traits trait DatabaseTrait { static DBConnection dbConnection def setupSpec() { //or @BeforeClass or inlined //prepare DB } def cleanupSpec() { //or @AfterClass //clean up DB } } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 45. Simplifying specifications with traits trait DatabaseTrait { static DBConnection dbConnection def setupSpec() { //or @BeforeClass or inlined //prepare DB } def cleanupSpec() { //or @AfterClass //clean up DB } } //no more infrastructure code needed in specifications 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/
  • 46. Simplifying specifications with traits - gotchas issue with obtaining annotations on fields some field-based extensions are not available @Sharedhas to be replaced with static @AutoCleanuphas to be replaced with cleanup(Spec) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 47. Parameterized tests Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 48. Parameterized 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 } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 49. Parameterized 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/
  • 50. Parameterized 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/
  • 51. Parameterized 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/
  • 52. Parameterized 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/
  • 53. Parameterized 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/
  • 54. Parameterized 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/
  • 55. Parameterized 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/
  • 56. Parameterized 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/
  • 57. Parameterized 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/
  • 58. Parameterized 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/
  • 59. Parameterized tests - @Unroll w/ 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/
  • 60. Parameterized tests - @Unroll w/ 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/
  • 61. Parameterized tests - @Unroll w/ 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/
  • 62. Parameterized 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/
  • 63. Parameterized tests - 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/
  • 64. Parameterized tests - @Unrollfor all unfortunately @Unrollis not enabled by default for simple cases can be put as a class level no side effects for not parameterized tests @Unroll class PeselValidatorSpec extends Specification { def "should pass valid PESEL numbers"() { ... } def "should reject too short PESEL numbers"() { ... } def "should reject wrong checksum PESEL numbers"() { ... } (...) } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 65. Mocking Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 66. 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/
  • 67. 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/
  • 68. 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/
  • 69. 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/
  • 70. Simple Stubbing - 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(_) >> { ??? } when: Item returnedItem = dao.save(baseItem) then: baseItem.is(returnedItem) } } How to just return return the first method execution argument? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 71. 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/
  • 72. 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/
  • 73. 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/
  • 74. 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/
  • 75. Stubbing and verifying together How to achieve it in code? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 76. Stubbing and verifying together class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao<Item> dao = Mock(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/
  • 77. 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"() { ... } def "should calculate important business case 3"() { ... } many positive cases with sendPing() >> "OK" how to test negative scenario? Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 78. 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/
  • 79. 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/
  • 80. 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/
  • 81. 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/
  • 82. 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/
  • 84. Active condition waiting PoolingConditions Active waiting for condition to pass - like in Awaitility Condition is checked multiple times per second No more sleep(3000) Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 85. 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/
  • 86. 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/
  • 87. 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/
  • 88. Active condition waiting PoolingConditions Active waiting for condition to pass - like in Awaitility Condition is checked multiple times per second No more sleep(3000) Ability to specify: timeout, initialDelay, delay and factor assertkeyword is (usually) required Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 89. Active condition waiting PoolingConditions Active waiting for condition to pass - like in Awaitility Condition is checked multiple times per second 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
  • 90. Asserting previous value (manual way) Check if/how given value was changed Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 91. 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/
  • 92. 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) } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 93. 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/
  • 94. 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/
  • 95. Capturing thrown exception - long syntax def "should capture exception - long form"() { when: throwNPE() then: NullPointerException e = thrown(NullPointerException) e.message == "test NPE" } Capture thrown exception (and bind it to variable) Fails the test if no exception was thrown Fails the test if other exception was thrown Requires redundant information Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 96. Capturing thrown exception - short syntax def "should capture exception - shorten 1"() { when: throwNPE() then: def e = thrown(NullPointerException) e.message == "test NPE" } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 97. Capturing thrown exception - short syntax def "should capture exception - shorten 1"() { when: throwNPE() then: def e = thrown(NullPointerException) e.message == "test NPE" } def "should capture exception - shorten 2"() { when: throwNPE() then: NullPointerException e = thrown() e.message == "test NPE" } Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 98. Capturing thrown exception - short syntax def "should capture exception - shorten 1"() { when: throwNPE() then: def e = thrown(NullPointerException) e.message == "test NPE" } def "should capture exception - shorten 2"() { when: throwNPE() then: NullPointerException e = thrown() e.message == "test NPE" } Identical behavior to the long one Choose which you like best Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 99. 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/
  • 100. 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
  • 101. 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 (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/
  • 102. Summary Flat learning curve to start using Spock, but... Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 103. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 104. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Multiple cool features waiting to be discovered Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 105. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Multiple cool features waiting to be discovered For everyday use Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 106. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Multiple cool features waiting to be discovered For everyday use For just one particular thing Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 107. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Multiple cool features waiting to be discovered For everyday use For just one particular thing But done in the awesome way Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 108. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Multiple cool features waiting to be discovered For everyday use For just one particular thing But done in the awesome way Know your tools Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 109. Summary Flat learning curve to start using Spock, but... ... this is just the beginning Multiple cool features waiting to be discovered For everyday use For just one particular thing But done in the awesome way Know your tools ... and use Spock in even more effective way Marcin Zajączkowski @SolidSoftBlog http://blog.solidsoft.info/
  • 111. 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/