Kotlin allows you to put method implementation in an interface. The same mechanism can be found in Java interfaces as default methods (and also Groovy or Scala traits). Read more
Tag: Spock
Testing Kotlin with Spock Part 2 – Enum with instance method
Testing Kotlin with Spock Part 2 – Enum instance method
The enum class with instance method in Kotlin is quite similar to its Java version, but they are look a bit different Read more
Testing Kotlin with Spock Part 1 – Object
The object
keyword in Kotlin creates singleton in a very convenient way. It can be used for example as a state of an operation. Spock Framework is one of the most expressive Read more
Groovy, Callable and ExecutorService
The Baroque version
You could create a class that implements Callable:class MyJob implements Callable<Integer> {
@Override
Integer call() throws Exception {
return 42
}
}
and give it to the executor service:
def 'submit callable as MyJob object'() {
expect:
executorService.submit(new MyJob()).get() == 42
}
The response is, as expected, 42.
Map as Callable version
You want to use this job only in one place so why not inline this class:def 'submit callable as map'() {
expect:
executorService.submit([call: { 42 }] as Callable).get() == 42
}
The response is again 42.
Groovy closure version
Why not use closure instead of map?def 'submit callable as closure'(){
expect:
executorService.submit { 42 }.get() == 42
}
The response is ... null.
Condition not satisfied:
executorService.submit { 42 }.get() == 42
| | | |
| | null false
| java.util.concurrent.FutureTask@21de60b4
java.util.concurrent.Executors$FinalizableDelegatedExecutorService@1700915
Why? It is because Groovy treats this closure as Runnable, not Callable and Future#get returns null when task is complete.
Groovy closure version with cast
We have to cast our closure before submiting to executor service:def 'submit callable as closure with cast'() {
when:
int result = executorService.submit({ return 42 } as Callable<Integer>).get()
then:
result == 42
}
The response is, as expected, again 42.What interesting, the same test with inlined result variable fails... Strange... It could be Spock framework error.
Source code is available here.
Grails with Spock unit test + IntelliJ IDEA = No thread-bound request found
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.codehaus.groovy.grails.plugins.web.api.CommonWebApi.currentRequestAttributes(CommonWebApi.java:205)
at org.codehaus.groovy.grails.plugins.web.api.CommonWebApi.getParams(CommonWebApi.java:65)
... // and few more lines of stacktrace ;)
Spock basics
So how can I start?
Let's go!
import spock.lang.*
class UserSpec extends Specification {
}
Now we can proceed to defining test fixtures and test methods.class UserSpec extends Specification {
User user
Document document
def setup() {
user = new User()
document = DocumentTestFactory.createDocumentWithTitle("doc1")
}
def cleanup() {
}
}
Of course we can use field initialization for instantiating test objects:class UserSpec extends Specification {
User user = new User()
Document document = DocumentTestFactory.createDocumentWithTitle("doc1")
def setup() {
}
def cleanup() {
}
}
First tests
class UserSpec extends Specification {
// ...
def "should assign coment to user"() {
// ...
}
}
With such naming convention we can write real specification and include details about specified behaviour in method name, what is very convenient when reading test reports and analyzing errors.
Test method (also called feature method) is logically divided into few blocks, each with its own purpose. Blocks are defined like labels in Java (but they are transformed with Groovy AST transform features) and some of them must be put in code in specific order.
Most basic and common schema for Spock test is:
class UserSpec extends Specification {
// ...
def "should assign coment to user"() {
given:
// do initialization of test objects
when:
// perform actions to be tested
then:
// collect and analyze results
}
}
But there are more blocks like:
- setup
- expect
- where
- cleanup
given block
class UserSpec extends Specification {
// ...
def "should add project to user and mark user as project's owner"() {
given:
User user = new User()
Project project = ProjectTestFactory.createProjectWithName("simple project")
// ...
}
}
In this code given block contains initialization of test objects and nothing more. We create simple user without any specified attributes and project with given name. In case when some of these objects could be reused in more feature methods, it could be worth putting initialization in setup method.
when and then blocks
class UserSpec extends Specification {
// ...
def "should assign user to comment when adding comment to user"() {
given:
User user = new User()
Comment comment = new Comment()
when:
user.addComment(comment)
then:
comment.getUserWhoCreatedComment().equals(user)
}
// ...
}
In when block there is a call of tested method and nothing more. After we are sure our action was performed, we can check for desired conditions in then block.
Then block is very well structured and its every line is treated by Spock as boolean statement. That means, Spock expects that we write instructions containing comparisons and expressions returning true or false, so we can create then block with such statements:
user.getName() == "John"
user.getAge() == 40
!user.isEnabled()
Each of lines will be treated as single assertion and will be evaluated by Spock.
Sometimes we expect that our method throws an exception under given circumstances. We can write test for it with use of thrown method:
class CommentSpec extends Specification {
def "should throw exception when adding null document to comment"() {
given:
Comment comment = new Comment()
when:
comment.setCommentedDocument(null)
then:
thrown(RuntimeException)
}
}
In this test we want to make sure that passing incorrect parameters is correctly handled by tested method and that method throws an exception in response. In case you want to be certain that method does not throw particular exception, simply use notThrown method.
expect block
def "should create user with given name"() {
given:
User user = UserTestFactory.createUser("john doe")
expect:
user.getName() == "john doe"
}
More blocks!
That were very simple tests with standard Spock test layout and canonical divide into given/when/then parts. But Spock offers more possibilities in writing tests and provides more blocks.setup/cleanup blocks
where - easy way to create readable parameterized tests
def "should successfully validate emails with valid syntax"() {
expect:
emailValidator.validate(email) == true
where:
email << [ "test@test.com", "foo@bar.com" ]
}
In this example, Spock creates variable called email which is used when calling method being tested. Internally feature method is called once, but framework iterates over given values and calls expect/when block as many times as there are values (however, if we use @Unroll annotation Spock can create separate run for each of given values, more about it in one of next examples).
Now, lets assume that we want our feature method to test both successful and failure validations. To achieve that goal we can create few parameterized variables for both input parameter and expected result. Here is a little example:
def "should perform validation of email addresses"() {
expect:
emailValidator.validate(email) == result
where:
email << [ "WTF", "@domain", "foo@bar.com" "a@test"
result << [ false, false, true, false ]
}
Well, it looks nice, but Spock can do much better. It offers tabular format of defining parameters for test what is much more readable and natural. Lets take a look:
def "should perform validation of email addresses"() {
expect:
emailValidator.validate(email) == result
where:
email | result
"WTF" | false
"@domain" | false
"foo@bar.com" | true
"a@test" | false
}
In this code, each column of our "table" is treated as a separate variable and rows are values for subsequent test iterations.
Another useful feature of Spock during parameterizing test is its ability to "unroll" each parameterized test. Feature method from previous example could be defined as (the body stays the same, so I do not repeat it):
@Unroll("should validate email #email")
def "should perform validation of email addresses"() {
// ...
}
With that annotation, Spock generate few methods each with its own name and run them separately. We can use symbols from where blocks in @Unroll argument by preceding it with '#' sign what is a signal to Spock to use it in generated method name.
What next?
Spock, Java and Maven
<dependency>
<groupid>org.spockframework</groupId>
<artifactid>spock-core</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-all</artifactId>
<version>${groovy.version}</version>
<scope>test</scope>
</dependency>
Where groovy.version is property defined in pom.xml for more convenient use and easy version change, just like this:
<properties>
<gmaven-plugin.version>1.4</gmaven-plugin.version>
<groovy.version>2.1.5</groovy.version>
</properties>
- cglib - for class mocking
- objenesis - enables mocking classes without default constructor
<dependency>
<groupid>cglib</groupId>
<artifactid>cglib-nodep</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.objenesis</groupId>
<artifactid>objenesis</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<plugin>
<groupid>org.codehaus.gmaven</groupId>
<artifactid>gmaven-plugin</artifactId>
<version>${gmaven-plugin.version}</version>
<configuration>
<providerselection>2.0</providerSelection>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupid>org.codehaus.gmaven.runtime</groupId>
<artifactid>gmaven-runtime-2.0</artifactId>
<version>${gmaven-plugin.version}</version>
<exclusions>
<exclusion>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-all</artifactId>
<version>${groovy.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupid>org.apache.maven.plugins</groupId>
<artifactid>maven-surefire-plugin</artifactId>
<version>2.15</version>
<configuration>
<includes>
<include>**/*Test.java</include>
<include>**/*Spec.java</include>
</includes>
</configuration>
</plugin>
<include>**/*Test.class</include>
<include>**/*Spec.class</include>
(thanks to Tomek Pęksa for pointing this out!)How to keep session in HttpBuilder with cookies
In my real-world scenario I have a REST service for AJAX purposes. It renders data series for graphs. I want to test it with groovy's excellent HttpBuilder. There is a problem though - these requests are only available for already logged in users.
In this post I present a complete solution to maintain a session state between HttpBuilder's requests.
Session in HttpBuilder
First of all a quick reminder about session. Session is a simulation of state for HTTP requests, which are stateless by its nature. Once you log in you receive a unique cookie (one or more) that identifies you for sequential requests. Every time you send request you send this cookie along. This way server recognizes you and matches you to your session, which is kept on server. Cookie gets invlid once you log out or it times out, for example after 20 minutes of inactivity. Next time you visit a page you get a new, unique cookie.
In order to keep session alive in HttpBuilder I need to:
- log in to my Grails application
- receive a JSESSIONID cookie in response
- store that cookie and send it along with every subsenquential request
I've created RestConnector
class that wraps up HttpBuilder. It's main improvement is that it keeps received cookie in a list.
package eu.spoonman.connectors.RestConnector
import groovyx.net.http.Method
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator
class RestConnector {
private String baseUrl
private HTTPBuilder httpBuilder
private List<String> cookies
RestConnector(String url) {
this.baseUrl = url
this.httpBuilder = initializeHttpBuilder()
this.cookies = []
}
public def request(Method method, ContentType contentType, String url, Map<String, Serializable> params) {
debug("Send $method request to ${this.baseUrl}$url: $params")
httpBuilder.request(method, contentType) { request ->
uri.path = url
uri.query = params
headers['Cookie'] = cookies.join(';')
}
}
private HTTPBuilder initializeHttpBuilder() {
def httpBuilder = new HTTPBuilder(baseUrl)
httpBuilder.handler.success = { HttpResponseDecorator resp, reader ->
resp.getHeaders('Set-Cookie').each {
//[Set-Cookie: JSESSIONID=E68D4799D4D6282F0348FDB7E8B88AE9; Path=/frontoffice/; HttpOnly]
String cookie = it.value.split(';')[0]
debug("Adding cookie to collection: $cookie")
cookies.add(cookie)
}
debug("Response: ${reader}")
return reader
}
return httpBuilder
}
private debug(String message) {
System.out.println(message) //for Gradle
}
}
A few things to notice in a class above. Constructor sets base URL and creates HttpBuilder instance that can be reused. Next, there is a handler on successful request that checks if I receive any cookie. It adds received cookies to list. Finally, there is a request
method that calls HttpBuilder#request
but it adds cookies to HTTP headers so server can recognize me as a logged in user.
Sending cookies with every request is a core component in here. It simulates browser's behavior and maintains session.
How to use it?
I will show you how to use this utility class it in Spock test below. It is fairly simple.
First I login to my application and I ensure that I receive a cookie in return, which is equivalent to being logged in. Then I send a request with that cookie sent in HTTP header. This is a Spock test that implements it:
package eu.spoonman.specs.rest
import eu.spoonman.connectors.RestConnector.RestConnector
import groovyx.net.http.ContentType
import groovyx.net.http.Method
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise
@Stepwise
class RestChartSpec extends Specification {
@Shared
RestConnector restConnector
def setupSpec() {
restConnector = new RestConnector('http://localhost:8080')
}
def "should login as test"() {
given:
Map params = [j_username: 'test', j_password: 'test']
when:
restConnector.request(Method.POST, ContentType.ANY, '/frontoffice/j_spring_security_check', params)
then:
!(restConnector.cookies.empty)
}
def "should allow access to chart data series"() {
given:
Map params = [days: 14]
when:
Map result = restConnector.request(Method.POST, ContentType.JSON, "frontoffice/chart/series", params)
then:
result != null
result.series.size() > 0
}
}
I create a new RestConnector
instance in setupSpec
with my application's base URL. Please notice that it has @Shared
annotation so it's shared between tests.
@Stepwise
is crucial annotation for this specification. It means that Spock executes tests exactly in order they're defined. I need to ensure that login is executed first. I also need to assert that I receive a cookie and list is not empty. I could move this step into setupSpec
method too, but I prefer it to be a first test in a specification.
Second test is always executed after login thus it sends cookies within request headers. This is exactly what I wanted to achieve.
How to keep session in HttpBuilder with cookies
In my real-world scenario I have a REST service for AJAX purposes. It renders data series for graphs. I want to test it with groovy's excellent HttpBuilder. There is a problem though - these requests are only available for already logged in users.
In this post I present a complete solution to maintain a session state between HttpBuilder's requests.
Session in HttpBuilder
First of all a quick reminder about session. Session is a simulation of state for HTTP requests, which are stateless by its nature. Once you log in you receive a unique cookie (one or more) that identifies you for sequential requests. Every time you send request you send this cookie along. This way server recognizes you and matches you to your session, which is kept on server. Cookie gets invlid once you log out or it times out, for example after 20 minutes of inactivity. Next time you visit a page you get a new, unique cookie.
In order to keep session alive in HttpBuilder I need to:
- log in to my Grails application
- receive a JSESSIONID cookie in response
- store that cookie and send it along with every subsenquential request
I've created RestConnector
class that wraps up HttpBuilder. It's main improvement is that it keeps received cookie in a list.
package eu.spoonman.connectors.RestConnector
import groovyx.net.http.Method
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator
class RestConnector {
private String baseUrl
private HTTPBuilder httpBuilder
private List<String> cookies
RestConnector(String url) {
this.baseUrl = url
this.httpBuilder = initializeHttpBuilder()
this.cookies = []
}
public def request(Method method, ContentType contentType, String url, Map<String, Serializable> params) {
debug("Send $method request to ${this.baseUrl}$url: $params")
httpBuilder.request(method, contentType) { request ->
uri.path = url
uri.query = params
headers['Cookie'] = cookies.join(';')
}
}
private HTTPBuilder initializeHttpBuilder() {
def httpBuilder = new HTTPBuilder(baseUrl)
httpBuilder.handler.success = { HttpResponseDecorator resp, reader ->
resp.getHeaders('Set-Cookie').each {
//[Set-Cookie: JSESSIONID=E68D4799D4D6282F0348FDB7E8B88AE9; Path=/frontoffice/; HttpOnly]
String cookie = it.value.split(';')[0]
debug("Adding cookie to collection: $cookie")
cookies.add(cookie)
}
debug("Response: ${reader}")
return reader
}
return httpBuilder
}
private debug(String message) {
System.out.println(message) //for Gradle
}
}
A few things to notice in a class above. Constructor sets base URL and creates HttpBuilder instance that can be reused. Next, there is a handler on successful request that checks if I receive any cookie. It adds received cookies to list. Finally, there is a request
method that calls HttpBuilder#request
but it adds cookies to HTTP headers so server can recognize me as a logged in user.
Sending cookies with every request is a core component in here. It simulates browser's behavior and maintains session.
How to use it?
I will show you how to use this utility class it in Spock test below. It is fairly simple.
First I login to my application and I ensure that I receive a cookie in return, which is equivalent to being logged in. Then I send a request with that cookie sent in HTTP header. This is a Spock test that implements it:
package eu.spoonman.specs.rest
import eu.spoonman.connectors.RestConnector.RestConnector
import groovyx.net.http.ContentType
import groovyx.net.http.Method
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise
@Stepwise
class RestChartSpec extends Specification {
@Shared
RestConnector restConnector
def setupSpec() {
restConnector = new RestConnector('http://localhost:8080')
}
def "should login as test"() {
given:
Map params = [j_username: 'test', j_password: 'test']
when:
restConnector.request(Method.POST, ContentType.ANY, '/frontoffice/j_spring_security_check', params)
then:
!(restConnector.cookies.empty)
}
def "should allow access to chart data series"() {
given:
Map params = [days: 14]
when:
Map result = restConnector.request(Method.POST, ContentType.JSON, "frontoffice/chart/series", params)
then:
result != null
result.series.size() > 0
}
}
I create a new RestConnector
instance in setupSpec
with my application's base URL. Please notice that it has @Shared
annotation so it's shared between tests.
@Stepwise
is crucial annotation for this specification. It means that Spock executes tests exactly in order they're defined. I need to ensure that login is executed first. I also need to assert that I receive a cookie and list is not empty. I could move this step into setupSpec
method too, but I prefer it to be a first test in a specification.
Second test is always executed after login thus it sends cookies within request headers. This is exactly what I wanted to achieve.