#ATAGTR2016 Global Testing Retreat Pune 2016 © S
COOL JVM TOOLS TO HELP
YOU TEST
Schalk W. Cronjé
1
ABOUT ME
Email:
Twitter / Ello : @ysb33r
ysb33r@gmail.com
2
TECHNICAL TESTER
Work on coding skills
Think
Be open-minded
Testing is even more important that development.
3
LET’S REVIEW OUR TESTS
4 . 1
LET’S GET UNIT TESTING
class CalculatorSpec extends Specification {
def "A calculator must be able to add numbers"() {
given: 'That I have a calculator'
def calc = new Calculator()
when: "I add 2, 3 and -20"
def answer = calc.plus 2,3,-20
then: "The answer should be -15"
answer == -15
}
}
4 . 2
WHEN THIS TEST FAILS
4 . 3
WHEN THIS TEST FAILS
4 . 4
SPOCK FRAMEWORK
Built on top of JUnit 4.x.
Use it with all your JUnit tools!
Use for more than just unit tests.
Mocks & stubs included
Easily test Java, Groovy, Kotlin, Scala etc.
4 . 5
DATA-DRIVEN TESTS
@Unroll
def "A calculator must be able to multiply"() {
given: 'That I have a multiplying calculator'
def calc = new Calculator()
expect: "The answer to be #answer"
answer == calc.multiply (a,b,c)
where: "The operation is #a * #b * #c"
a | b | c || answer
3 | 7 | 5 || 105
1 | 3 | 0 || 0
2 | -1| 1 || -2
}
4 . 6
ANOTHER COOL FAILURE REPORT
4 . 7
HANDLING EXCEPTIONS
def "Dividing by zero should throw an exception"() {
given: 'That I have a dividing calculator'
def calc = new Calculator()
when: "I divide by zero"
calc.divide 1,0
then: "I expect an error"
thrown(ArithmeticException)
}
4 . 8
MOCK OUT INTERFACES
public interface RemoteCalculator {
public Number plus(Number... args);
}
def "Remote calculator"() {
given: "A remote calculator is available"
def calc = Mock(RemoteCalculator)
when: "Multiple items are sent for addition"
calc.plus 1,2,3,4,5
then: "The calculator is only called once"
1 * calc.plus(_)
}
4 . 9
SPOCK FRAMEWORK
Spock Framework:
Spock Reports
http://spockframework.github.io/spock/docs/1.0/index.html
https://github.com/renatoathaydes/spock-reports
4 . 10
WHAT IS THIS EVIL?
Underlying language is (Apache) Groovy
You don’t need to be a Groovy expert to be a power user of
many of these tools.
Groovy doesn’t need ; in most cases
Groovy does more with less punctuation, making it an ideal
choice for a DSL
In most cases lines that do not end on an operator is
considered a completed statement.
5 . 1
GROOVY VS JAVA
In Groovy:
All class members are public by default
No need to create getters/setters for public elds
Both static & dynamic typing supported
def means Object
5 . 2
CALLING METHODS
class Foo {
void bar( def a,def b ) {}
}
def foo = new Foo()
foo.bar( '123',456 )
foo.bar '123', 456
foo.with {
bar '123', 456
}
5 . 3
CALLING METHODS WITH CLOSURES
class Foo {
void bar( def a,Closure b ) {}
}
def foo = new Foo()
foo.bar( '123',{ println it } )
foo.bar ('123') {
println it
}
foo.bar '123', {
println it
}
5 . 4
MAPS IN GROOVY
Hashmaps in Groovy are simple to use
def myMap = [ plugin : 'java' ]
Maps are easy to pass inline to functions
project.apply( plugin : 'java' )
Which can also be written as
project.with {
apply plugin : 'java'
}
5 . 5
LISTS IN GROOVY
Lists in Groovy are simple too
def myList = [ 'clone', 'http://github.com/ysb33r/GradleLectures' ]
This makes it possible write a method call as
args 'clone', 'http://github.com/ysb33r/GradleLectures'
5 . 6
CLOSURE DELEGATION IN GROOVY
When a symbol cannot be resolved within a closure,
Groovy will look elsewhere
In Groovy speak this is called a Delegate.
This can be programmatically controlled via the
Closure.delegate property.
5 . 7
CLOSURE DELEGATION IN GROOVY
class Foo {
def target
}
class Bar {
Foo foo = new Foo()
void doSomething( Closure c ) {
c.delegate = foo
c()
}
}
Bar bar = new Bar()
bar.doSomething {
target = 10
}
5 . 8
MORE CLOSURE MAGIC
If a Groovy class has a method call(Closure), the object
can be passed a closure directly.
class Foo {
def call( Closure c) { /* ... */ }
}
Foo foo = new Foo()
foo {
println 'Hello, world'
}
// This avoids ugly syntax
foo.call({ println 'Hello, world' })
5 . 9
6 . 1
QUICK HTTP TESTING
QUICK HTTP TESTING
Quickly point to a remote or in-process server
Build payload in a simplistic manner
Execute the verb
Check the response in a readable manner
6 . 2
QUICK HTTP TESTING
def "The echo path should return what is send to it"() {
given: "A simple text request"
requestSpec { pay ->
pay.body.type(MediaType.PLAIN_TEXT_UTF8).text(' ')
}
when: "The data is posted"
post '/'
then: "It should be echoed back"
response.statusCode == 200
response.body.text == 'You said: '
}
6 . 3
7 . 1
 
7 . 2
RATPACK FRAMEWORK
Set of Java libraries for building modern HTTP applications.
Built on Java 8, Netty and reactive principles.
Groovy syntax for those who prefer it.
7 . 3
RATPACK’S TESTHTTPCLIENT
Can be used standalone
Use maven coordinates:
io.ratpack:ratpack-test:1.2.0
RATPACK’S TESTHTTPCLIENT
ApplicationUnderTest app = new ApplicationUnderTest() {
@Override
URI getAddress() { "http://127.0.0.1:${PORT}".toURI() }
}
@Delegate TestHttpClient client = TestHttpClient.testHttpClient(app)
def "The echo path should return what is send to it"() {
given: "A simple text request"
requestSpec { pay ->
pay.body.type(MediaType.PLAIN_TEXT_UTF8).text(' ')
}
when: "The data is posted"
post '/'
then: "It should be echoed back"
response.statusCode == 200
response.body.text == 'You said: '
}
7 . 4
OFFLINE API TESTING
Sometimes you simply have to use a live site for testing
Mocking & stubbing simply not good enough
Need live data
Do not want to overload service with unnecessary test
data
Want to test of ine
8 . 1
OFFLINE API TESTING
@Betamax(tape='files')
def "Source jar must be posted"() {
given: 'The list of files is requested from the repository'
def list =
get path : "${REPO_ROOT}/bintray-gradle-plugin/files"
expect: 'The bintray source jar should be included'
list?.find {
it?.path ==
'org.ysb33r.gradle/bintray/0.0.5/bintray-0.0.5-sources.jar'
}
}
8 . 2
9 . 1
 
BETAMAX
Allows record & playpack
Mocks external HTTP resources
Web services
REST APIs
Stores recordings in YAML
Easy to edit
Commit to source control
Remember to sanitize auth credentials!
9 . 2
DEMO
9 . 3
9 . 4
CREATE RECORDER
@Shared ProxyConfiguration configuration = ProxyConfiguration.builder().
tapeRoot(new File('/path/to/tapes')).
ignoreLocalhost(false).
sslEnabled(true).
build()
@Rule RecorderRule recorder = new RecorderRule(configuration)
 
http://betamax.software
https://github.com/betamaxteam/betamax
9 . 5
WEBSITE TESTING
One of the most predominant area of testing of this decade
Test-driven webpage development is less effort in long run
Selenium is the leading tool
Selenium tests can be overly verbose
10 . 1
TRIVIAL TEST EXAMPLE
def "Learn about testing checkboxes"() {
when: "I go to that the-internet site"
go "${webroot}/checkboxes"
then: 'I am expecting Checkbox page'
$('h3').text() == 'Checkboxes'
and: 'The checkbox states are no & yes'
$(By.id('checkboxes')).$('input')*.@checked == ['','true']
}
10 . 2
11 . 1
 
GEB
Integrates with
Spock Framework
JUnit
TestNG
Cucumber-JVM
Makes Selenium readable
Anything you can do in Selenium you can do in Geb
11 . 2
TRIVIAL TEST EXAMPLE COMPLETE
class CheckboxExampleSpec extends GebSpec {
def "Learn about testing checkboxes"() {
when: "I go to that the-internet site"
go "${webroot}/checkboxes"
then: 'I am expecting Checkbox page'
$('h3').text() == 'Checkboxes'
and: 'The checkbox states are no & yes'
$(By.id('checkboxes')).$('input')*.@checked == ['','true']
}
}
11 . 3
GRABBING SCREENSHOTS
class CheckboxReportingSpec extends GebReportingSpec {
def 'Learn about testing checkboxes'() {
when: 'I go to that the-internet site'
go "${webroot}/checkboxes"
report 'checkbox-screen'
then: 'I am expecting Checkbox page'
$('h3').text() == 'Checkboxes'
and: 'The checkbox states are no & yes'
$(By.id('checkboxes')).$('input')*.@checked == ['','true']
}
}
Keep record during test
Stores HTML & PNG.
11 . 4
GRABBING SCREENSHOTS
11 . 5
 
@GemFramework
http://gebish.org
11 . 6
TIE IT TOGETHER
Automation is very important for effectiveness and time-
to-market
Ability to run all tests locally, under CI and under special
environments
Modern testers needs to know about deployment
12
13 . 1
 
13 . 2
GRADLE
Very modern, next-generation build & deployment pipeline
tool
DSL is based on Groovy
Vast collection of plugins
GRADLE
apply plugin : 'groovy'
repositories {
jcenter()
}
dependencies {
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile "org.gebish:geb-spock:0.13.0"
testCompile "org.seleniumhq.selenium:selenium-api:2.53.0"
testRuntime "org.seleniumhq.selenium:selenium-support:2.53.0"
testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:2.53.0"
}
13 . 3
GRADLE
Make it easy to integrate development and complex testing
Deploy local & to remote sites via plugins
Use Docker local & remote
"I do not have means to test on my machine" becomes less
of an argument.
13 . 4
 
Website:
Plugins Portal:
User Forum:
@gradle
@DailyGradle
http://gradle.org
http://plugins.gradle.org
http://discuss.gradle.org
13 . 5
14
DEMO
 
http://leanpub.com/idiomaticgradle
15
ABOUT THIS PRESENTATION
Written in Asciidoctor (1.5.3.2)
Styled by asciidoctor-revealjs extension
Built using:
Gradle
gradle-asciidoctor-plugin
gradle-vfs-plugin
All code snippets tested as part of build
16
THANK YOU
Schalk Cronjé
ysb33r@gmail.com
@ysb33r

Cool JVM Tools to Help You Test

  • 1.
    #ATAGTR2016 Global TestingRetreat Pune 2016 © S COOL JVM TOOLS TO HELP YOU TEST Schalk W. Cronjé
  • 2.
    1 ABOUT ME Email: Twitter /Ello : @ysb33r ysb33r@gmail.com
  • 3.
    2 TECHNICAL TESTER Work oncoding skills Think Be open-minded Testing is even more important that development.
  • 4.
  • 5.
    4 . 1 LET’SGET UNIT TESTING class CalculatorSpec extends Specification { def "A calculator must be able to add numbers"() { given: 'That I have a calculator' def calc = new Calculator() when: "I add 2, 3 and -20" def answer = calc.plus 2,3,-20 then: "The answer should be -15" answer == -15 } }
  • 6.
    4 . 2 WHENTHIS TEST FAILS
  • 7.
    4 . 3 WHENTHIS TEST FAILS
  • 8.
    4 . 4 SPOCKFRAMEWORK Built on top of JUnit 4.x. Use it with all your JUnit tools! Use for more than just unit tests. Mocks & stubs included Easily test Java, Groovy, Kotlin, Scala etc.
  • 9.
    4 . 5 DATA-DRIVENTESTS @Unroll def "A calculator must be able to multiply"() { given: 'That I have a multiplying calculator' def calc = new Calculator() expect: "The answer to be #answer" answer == calc.multiply (a,b,c) where: "The operation is #a * #b * #c" a | b | c || answer 3 | 7 | 5 || 105 1 | 3 | 0 || 0 2 | -1| 1 || -2 }
  • 10.
    4 . 6 ANOTHERCOOL FAILURE REPORT
  • 11.
    4 . 7 HANDLINGEXCEPTIONS def "Dividing by zero should throw an exception"() { given: 'That I have a dividing calculator' def calc = new Calculator() when: "I divide by zero" calc.divide 1,0 then: "I expect an error" thrown(ArithmeticException) }
  • 12.
    4 . 8 MOCKOUT INTERFACES public interface RemoteCalculator { public Number plus(Number... args); } def "Remote calculator"() { given: "A remote calculator is available" def calc = Mock(RemoteCalculator) when: "Multiple items are sent for addition" calc.plus 1,2,3,4,5 then: "The calculator is only called once" 1 * calc.plus(_) }
  • 13.
    4 . 9 SPOCKFRAMEWORK Spock Framework: Spock Reports http://spockframework.github.io/spock/docs/1.0/index.html https://github.com/renatoathaydes/spock-reports
  • 14.
    4 . 10 WHATIS THIS EVIL? Underlying language is (Apache) Groovy You don’t need to be a Groovy expert to be a power user of many of these tools. Groovy doesn’t need ; in most cases Groovy does more with less punctuation, making it an ideal choice for a DSL In most cases lines that do not end on an operator is considered a completed statement.
  • 15.
    5 . 1 GROOVYVS JAVA In Groovy: All class members are public by default No need to create getters/setters for public elds Both static & dynamic typing supported def means Object
  • 16.
    5 . 2 CALLINGMETHODS class Foo { void bar( def a,def b ) {} } def foo = new Foo() foo.bar( '123',456 ) foo.bar '123', 456 foo.with { bar '123', 456 }
  • 17.
    5 . 3 CALLINGMETHODS WITH CLOSURES class Foo { void bar( def a,Closure b ) {} } def foo = new Foo() foo.bar( '123',{ println it } ) foo.bar ('123') { println it } foo.bar '123', { println it }
  • 18.
    5 . 4 MAPSIN GROOVY Hashmaps in Groovy are simple to use def myMap = [ plugin : 'java' ] Maps are easy to pass inline to functions project.apply( plugin : 'java' ) Which can also be written as project.with { apply plugin : 'java' }
  • 19.
    5 . 5 LISTSIN GROOVY Lists in Groovy are simple too def myList = [ 'clone', 'http://github.com/ysb33r/GradleLectures' ] This makes it possible write a method call as args 'clone', 'http://github.com/ysb33r/GradleLectures'
  • 20.
    5 . 6 CLOSUREDELEGATION IN GROOVY When a symbol cannot be resolved within a closure, Groovy will look elsewhere In Groovy speak this is called a Delegate. This can be programmatically controlled via the Closure.delegate property.
  • 21.
    5 . 7 CLOSUREDELEGATION IN GROOVY class Foo { def target } class Bar { Foo foo = new Foo() void doSomething( Closure c ) { c.delegate = foo c() } } Bar bar = new Bar() bar.doSomething { target = 10 }
  • 22.
    5 . 8 MORECLOSURE MAGIC If a Groovy class has a method call(Closure), the object can be passed a closure directly. class Foo { def call( Closure c) { /* ... */ } } Foo foo = new Foo() foo { println 'Hello, world' } // This avoids ugly syntax foo.call({ println 'Hello, world' })
  • 23.
    5 . 9 6. 1 QUICK HTTP TESTING
  • 24.
    QUICK HTTP TESTING Quicklypoint to a remote or in-process server Build payload in a simplistic manner Execute the verb Check the response in a readable manner
  • 25.
    6 . 2 QUICKHTTP TESTING def "The echo path should return what is send to it"() { given: "A simple text request" requestSpec { pay -> pay.body.type(MediaType.PLAIN_TEXT_UTF8).text(' ') } when: "The data is posted" post '/' then: "It should be echoed back" response.statusCode == 200 response.body.text == 'You said: ' }
  • 26.
    6 . 3 7. 1  
  • 27.
    7 . 2 RATPACKFRAMEWORK Set of Java libraries for building modern HTTP applications. Built on Java 8, Netty and reactive principles. Groovy syntax for those who prefer it.
  • 28.
    7 . 3 RATPACK’STESTHTTPCLIENT Can be used standalone Use maven coordinates: io.ratpack:ratpack-test:1.2.0
  • 29.
    RATPACK’S TESTHTTPCLIENT ApplicationUnderTest app= new ApplicationUnderTest() { @Override URI getAddress() { "http://127.0.0.1:${PORT}".toURI() } } @Delegate TestHttpClient client = TestHttpClient.testHttpClient(app) def "The echo path should return what is send to it"() { given: "A simple text request" requestSpec { pay -> pay.body.type(MediaType.PLAIN_TEXT_UTF8).text(' ') } when: "The data is posted" post '/' then: "It should be echoed back" response.statusCode == 200 response.body.text == 'You said: ' }
  • 30.
    7 . 4 OFFLINEAPI TESTING Sometimes you simply have to use a live site for testing Mocking & stubbing simply not good enough Need live data Do not want to overload service with unnecessary test data Want to test of ine
  • 31.
    8 . 1 OFFLINEAPI TESTING @Betamax(tape='files') def "Source jar must be posted"() { given: 'The list of files is requested from the repository' def list = get path : "${REPO_ROOT}/bintray-gradle-plugin/files" expect: 'The bintray source jar should be included' list?.find { it?.path == 'org.ysb33r.gradle/bintray/0.0.5/bintray-0.0.5-sources.jar' } }
  • 32.
    8 . 2 9. 1  
  • 33.
    BETAMAX Allows record &playpack Mocks external HTTP resources Web services REST APIs Stores recordings in YAML Easy to edit Commit to source control Remember to sanitize auth credentials!
  • 34.
  • 35.
    9 . 3 9. 4 CREATE RECORDER @Shared ProxyConfiguration configuration = ProxyConfiguration.builder(). tapeRoot(new File('/path/to/tapes')). ignoreLocalhost(false). sslEnabled(true). build() @Rule RecorderRule recorder = new RecorderRule(configuration)
  • 36.
  • 37.
    9 . 5 WEBSITETESTING One of the most predominant area of testing of this decade Test-driven webpage development is less effort in long run Selenium is the leading tool Selenium tests can be overly verbose
  • 38.
    10 . 1 TRIVIALTEST EXAMPLE def "Learn about testing checkboxes"() { when: "I go to that the-internet site" go "${webroot}/checkboxes" then: 'I am expecting Checkbox page' $('h3').text() == 'Checkboxes' and: 'The checkbox states are no & yes' $(By.id('checkboxes')).$('input')*.@checked == ['','true'] }
  • 39.
    10 . 2 11. 1  
  • 40.
    GEB Integrates with Spock Framework JUnit TestNG Cucumber-JVM MakesSelenium readable Anything you can do in Selenium you can do in Geb
  • 41.
    11 . 2 TRIVIALTEST EXAMPLE COMPLETE class CheckboxExampleSpec extends GebSpec { def "Learn about testing checkboxes"() { when: "I go to that the-internet site" go "${webroot}/checkboxes" then: 'I am expecting Checkbox page' $('h3').text() == 'Checkboxes' and: 'The checkbox states are no & yes' $(By.id('checkboxes')).$('input')*.@checked == ['','true'] } }
  • 42.
    11 . 3 GRABBINGSCREENSHOTS class CheckboxReportingSpec extends GebReportingSpec { def 'Learn about testing checkboxes'() { when: 'I go to that the-internet site' go "${webroot}/checkboxes" report 'checkbox-screen' then: 'I am expecting Checkbox page' $('h3').text() == 'Checkboxes' and: 'The checkbox states are no & yes' $(By.id('checkboxes')).$('input')*.@checked == ['','true'] } } Keep record during test Stores HTML & PNG.
  • 43.
    11 . 4 GRABBINGSCREENSHOTS
  • 44.
  • 45.
    11 . 6 TIEIT TOGETHER Automation is very important for effectiveness and time- to-market Ability to run all tests locally, under CI and under special environments Modern testers needs to know about deployment
  • 46.
  • 47.
    13 . 2 GRADLE Verymodern, next-generation build & deployment pipeline tool DSL is based on Groovy Vast collection of plugins
  • 48.
    GRADLE apply plugin :'groovy' repositories { jcenter() } dependencies { testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' testCompile "org.gebish:geb-spock:0.13.0" testCompile "org.seleniumhq.selenium:selenium-api:2.53.0" testRuntime "org.seleniumhq.selenium:selenium-support:2.53.0" testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:2.53.0" }
  • 49.
    13 . 3 GRADLE Makeit easy to integrate development and complex testing Deploy local & to remote sites via plugins Use Docker local & remote "I do not have means to test on my machine" becomes less of an argument.
  • 50.
    13 . 4   Website: PluginsPortal: User Forum: @gradle @DailyGradle http://gradle.org http://plugins.gradle.org http://discuss.gradle.org
  • 51.
  • 52.
  • 53.
    15 ABOUT THIS PRESENTATION Writtenin Asciidoctor (1.5.3.2) Styled by asciidoctor-revealjs extension Built using: Gradle gradle-asciidoctor-plugin gradle-vfs-plugin All code snippets tested as part of build
  • 54.