Testy – nie takie straszne, jak je malująTests – not so scary as they used to

Java + Selenium
Java + Selenium

O czym będzie ?

O testach interfejsu aplikacji WEB’owych. Temat, choć związany z programowaniem, to niezbyt popularny wśród developerów. Pisanie testów uchodzi za zajęcie mniej ambitne, nudne, poboczne i częstokroć traktowane jest jako zło konieczne.

Testy to temat rzeka. Płynie ona w wielu serwisach i blogach w internecie. Nie będę więc zawracał tej rzeki i przepisywał internet na nowo. Doleję do niej tylko jedną ważną informację – odpowiem na pytanie, czy można zmienić powszechny stereotyp na temat testów interfejsu na przykładzie testów Selenium.

Pytanie to zrodziło się w mojej głowie, na przestrzeni kilku ostatnich lat. Zadawałem je sobie niejednokrotnie, myśląc, że odpowiedź jest oczywista i potwierdzająca stereotyp. Tak było, dopóki nie trafiłem do dużego projektu, w którym ktoś wreszcie ‘odpowiednio’ podszedł do tematu testów.

Myślisz ze znasz Selenium ?

Selenium to środowisko do obsługi testów frontendów aplikacji internetowych. W większości przypadków używane jest według scenariusza: nagraj test klikając po stronie, a potem odtwarzaj po każdej zmianie w kodzie. Niby ok, ale nie do końca.

Nagrane w ten sposób testy bardzo szybko stają się nieutrzymywalne. Wyobraźmy sobie 500 testów tego typu, a później wyobraźmy sobie, że trzeba zmienić interfejs aplikacji na inny. Inaczej rozmieszczone kontrolki, panele, inne ich nazwy id’ki itd. Przerobienie takiej ilości testów do nowej wersji aplikacji staje się praktycznie niemożliwe, a już na pewno nie opłacalne.

Jest kilka sposobów by radzić sobie z tego typu sytuacjami. Ja opiszę jedno konkretne rozwiązanie, które dla mnie, z punktu widzenia osoby piszącej w JAVIE, jest najbardziej wygodne. Testy Selenium można pisać właśnie w tym języku. ‘Dokładnie’ tak, jak zwykłe aplikacje. I to Dokładnie przez duże ‘D’, bo nie tylko korzystając z języka, ale też stosując wszelkie związane z nim dobre praktyki, wzorce projektowe, moc i funkcjonalność testów jednostkowych. Nie bez znaczenia jest też to, że testy można pisać i utrzymywać w ulubionym IDE, które nie tylko ułatwia pisanie, ale też i utrzymanie testów, serwując szereg narzędzi odpowiedzialnych za np. refaktoring, czy obsługę wersjonowania.

Testy – jak to się robi ?

Niechęć do testów bierze się głównie z dwóch powodów: ich pisanie jest nudne i żmudne. Obie opinie nie są niestety bezpodstawne, co pokażę cytując kod z tutoriala do Selenium+Java ze strony projektu: http://code.google.com/p/selenium/wiki/GettingStarted. Podkreślam, że nie chcę nikogo obrazić i wiem, że w poniższym kodzie jest kilka uproszczeń, niemniej jednak tak właśnie wygląda większość testów, które można znaleźć w internecie. Kto nie wierzy niech sobie wygoogla frazę: selenium java. [java] // Go to the Google Suggest home page driver.get(“http://www.google.com/webhp?complete=1&hl=en”); // Enter the query string “Cheese” WebElement query = driver.findElement(By.name(“q”)); query.sendKeys(“Cheese”); // Sleep until the div we want is visible or 5 seconds is over long end = System.currentTimeMillis() + 5000; while (System.currentTimeMillis() < end) { WebElement resultsDiv = driver.findElement(By.className(“gssb_e”)); // If results have been returned, the results are displayed in a drop down. if (resultsDiv.isDisplayed()) { break; } } // And now list the suggestions List allSuggestions = driver.findElements(By.xpath(“//td[@class=’gssb_a gbqfsf’]”)); for (WebElement suggestion : allSuggestions) { System.out.println(suggestion.getText()); } } [/java]

Co robi powyższy kod ? Na pierwszy rzut oka widać, że.. nic nie widać. Gdyby nie komentarze, to jego zrozumienie byłoby trudne. Wymagałoby nie tylko znajomości działania strony google, ale także jej kodu źródłowego. Nawet wtedy jednak, zrozumienie tej instrukcji: driver.findElements(By.xpath(“//td[@class=’gssb_a gbqfsf’]”)); zajełoby chwilę.

Testy – jak można to robić lepiej ?

Testy Selenium + Java wcale nie muszą być ani nudne, ani żmudne. Mogą wyglądać tak: [java] public void nieMoznaDodacEFakturyDlaPrepaida() { //given StarterPrepaid starter30Minut = new StarterPrepaid30Minut(); KatalogKart karty = Sklep.otworzSklep().menuGlowne.katalogKart(); //when Koszyk koszyk = karty.listaProduktow().wybierz(starter30Minut); //then assertFalse(koszyk.moznaDodacEfakture()); } [/java]

Już na pierwszy rzut oka widać o co chodzi w powyższym teście. Chodzi o to, by sprawdzić, czy możemy wybrać e-fakturę kupując ofertę na starter.

Pisząc ten test, analizując jego działanie, nie musimy wiedzieć, jak wygląda implementacja i kod źródłowy aplikacji. Nie obchodzi nas, który znacznik html musimy przeczytać, jakie są id’ki kolejnych przycisków, które trzeba kliknąć itd. Test jest na innym, wyższym poziomie abstrakcji. Operuje na działaniach biznesowych, bezpośrednio dotyczy funkcjonalności, którą testujemy.

Tak właśnie powinny być konstruowane testy Selenium. Najpierw należy poświęcić trochę czasu na napisanie odpowiedniej ‘domeny’, która pokryje funkcjonalność aplikacji, a dopiero potem pisać testy. Domena ta powinna udostępniać metody biznesowe, takie jak: kup, wyświetl, zaloguj, dodajDoKoszyka, usunZKoszyka itd. Metody takie powinny ukrywać bezpośrednią imlpementacje kliknięć, wyszukań po XPath, czy oczekiwań na przeładowanie strony www. Jednym słowem, mówiąc kolokwialnie, powinniśmy opakować selenium w coś bardziej wygodnego dla użytkownika naszej aplikacji.

A co z refaktoringiem ?

Oprócz ułatwień dla programistów, podniesienie testów na wyższy poziom abstrakcji, ma jeszcze jedną, kolosalną zaletę. Umożliwia bezpośrednie przeniesienie scenariuszy testowych na język aplikacji. Taki test nie tylko szybko się czyta, czy tworzy, ale bardzo łatwo weryfikuje i utrzymuje.

Na początku wspomniałem o trudnościach w dostosowaniu ‘typowych’ testów Selenium do zmian w interfejsie aplikacji. A co z refaktoringiem, przedstawionej przeze mnie konstrukcji testów ? Czy faktycznie jest on łatwiejszy ? Wyobraźmy więc sobie te 500 testów napisanych w taki odseparowany biznesowo sposób. Żaden z tych testów nie zawiera więc odnośników do kodu html, nie wykorzystuje bezpośrednio linków, ani nie zawiera odwołań do inputów, div’ów itd. W żadnym z tych 500 testów nie trzeba więc NIC zmieniać! Jedyne co trzeba zmienić, to domenę, część wspólną, która bazuje bezpośrednio na kodzie aplikacji. To jednak jest o niebo łatwiejsze niż zmiana 500 testów.

Czy to już wszsystko ?

Tak, to już wszystko. Celem niniejszego wpisu nie było pokazanie gotowych rozwiązań i ich implementacji krok po kroku. Celem było przekonanie do idei rozdzielenia testów na dwie warstwy: biznesową i tą związaną bezpośrednio z implementacją. Warto też zauważyć, że choć takie rozwiązanie nie jest często stosowane przy okazji testów, to jednak nie jest niczym nowym. Moim zdaniem warto je stosować, a już na pewno warto w połączeniu Selenium + Java.

Myślisz ze znasz Selenium ?

Selenium to środowisko do obsługi testów frontendów aplikacji internetowych. W większości przypadków używane jest według scenariusza: nagraj test klikając po stronie, a potem odtwarzaj po każdej zmianie w kodzie. Niby ok, ale nie do końca.

Nagrane w ten sposób testy bardzo szybko stają się nieutrzymywalne. Wyobraźmy sobie 500 testów tego typu, a później wyobraźmy sobie, że trzeba zmienić interfejs aplikacji na inny. Inaczej rozmieszczone kontrolki, panele, inne ich nazwy id’ki itd. Przerobienie takiej ilości testów do nowej wersji aplikacji staje się praktycznie niemożliwe, a już na pewno nie opłacalne.

Jest kilka sposobów by radzić sobie z tego typu sytuacjami. Ja opiszę jedno konkretne rozwiązanie, które dla mnie, z punktu widzenia osoby piszącej w JAVIE, jest najbardziej wygodne. Testy Selenium można pisać właśnie w tym języku. ‘Dokładnie’ tak, jak zwykłe aplikacje. I to Dokładnie przez duże ‘D’, bo nie tylko korzystając z języka, ale też stosując wszelkie związane z nim dobre praktyki, wzorce projektowe, moc i funkcjonalność testów jednostkowych. Nie bez znaczenia jest też to, że testy można pisać i utrzymywać w ulubionym IDE, które nie tylko ułatwia pisanie, ale też i utrzymanie testów, serwując szereg narzędzi odpowiedzialnych za np. refaktoring, czy obsługę wersjonowania.

Testy – jak to się robi ?

Niechęć do testów bierze się głównie z dwóch powodów: ich pisanie jest nudne i żmudne. Obie opinie nie są niestety bezpodstawne, co pokażę cytując kod z tutoriala do Selenium+Java ze strony projektu: http://code.google.com/p/selenium/wiki/GettingStarted. Podkreślam, że nie chcę nikogo obrazić i wiem, że w poniższym kodzie jest kilka uproszczeń, niemniej jednak tak właśnie wygląda większość testów, które można znaleźć w internecie. Kto nie wierzy niech sobie wygoogla frazę: selenium java. [java] // Go to the Google Suggest home page driver.get(“http://www.google.com/webhp?complete=1&hl=en”); // Enter the query string “Cheese” WebElement query = driver.findElement(By.name(“q”)); query.sendKeys(“Cheese”); // Sleep until the div we want is visible or 5 seconds is over long end = System.currentTimeMillis() + 5000; while (System.currentTimeMillis() < end) { WebElement resultsDiv = driver.findElement(By.className(“gssb_e”)); // If results have been returned, the results are displayed in a drop down. if (resultsDiv.isDisplayed()) { break; } } // And now list the suggestions List allSuggestions = driver.findElements(By.xpath(“//td[@class=’gssb_a gbqfsf’]”)); for (WebElement suggestion : allSuggestions) { System.out.println(suggestion.getText()); } } [/java]

Co robi powyższy kod ? Na pierwszy rzut oka widać, że.. nic nie widać. Gdyby nie komentarze, to jego zrozumienie byłoby trudne. Wymagałoby nie tylko znajomości działania strony google, ale także jej kodu źródłowego. Nawet wtedy jednak, zrozumienie tej instrukcji: driver.findElements(By.xpath(“//td[@class=’gssb_a gbqfsf’]”)); zajełoby chwilę.

Testy – jak można to robić lepiej ?

Testy Selenium + Java wcale nie muszą być ani nudne, ani żmudne. Mogą wyglądać tak: [java] public void nieMoznaDodacEFakturyDlaPrepaida() { //given StarterPrepaid starter30Minut = new StarterPrepaid30Minut(); KatalogKart karty = Sklep.otworzSklep().menuGlowne.katalogKart(); //when Koszyk koszyk = karty.listaProduktow().wybierz(starter30Minut); //then assertFalse(koszyk.moznaDodacEfakture()); } [/java]

Już na pierwszy rzut oka widać o co chodzi w powyższym teście. Chodzi o to, by sprawdzić, czy możemy wybrać e-fakturę kupując ofertę na starter.

Pisząc ten test, analizując jego działanie, nie musimy wiedzieć, jak wygląda implementacja i kod źródłowy aplikacji. Nie obchodzi nas, który znacznik html musimy przeczytać, jakie są id’ki kolejnych przycisków, które trzeba kliknąć itd. Test jest na innym, wyższym poziomie abstrakcji. Operuje na działaniach biznesowych, bezpośrednio dotyczy funkcjonalności, którą testujemy.

Tak właśnie powinny być konstruowane testy Selenium. Najpierw należy poświęcić trochę czasu na napisanie odpowiedniej ‘domeny’, która pokryje funkcjonalność aplikacji, a dopiero potem pisać testy. Domena ta powinna udostępniać metody biznesowe, takie jak: kup, wyświetl, zaloguj, dodajDoKoszyka, usunZKoszyka itd. Metody takie powinny ukrywać bezpośrednią imlpementacje kliknięć, wyszukań po XPath, czy oczekiwań na przeładowanie strony www. Jednym słowem, mówiąc kolokwialnie, powinniśmy opakować selenium w coś bardziej wygodnego dla użytkownika naszej aplikacji.

A co z refaktoringiem ?

Oprócz ułatwień dla programistów, podniesienie testów na wyższy poziom abstrakcji, ma jeszcze jedną, kolosalną zaletę. Umożliwia bezpośrednie przeniesienie scenariuszy testowych na język aplikacji. Taki test nie tylko szybko się czyta, czy tworzy, ale bardzo łatwo weryfikuje i utrzymuje.

Na początku wspomniałem o trudnościach w dostosowaniu ‘typowych’ testów Selenium do zmian w interfejsie aplikacji. A co z refaktoringiem, przedstawionej przeze mnie konstrukcji testów ? Czy faktycznie jest on łatwiejszy ? Wyobraźmy więc sobie te 500 testów napisanych w taki odseparowany biznesowo sposób. Żaden z tych testów nie zawiera więc odnośników do kodu html, nie wykorzystuje bezpośrednio linków, ani nie zawiera odwołań do inputów, div’ów itd. W żadnym z tych 500 testów nie trzeba więc NIC zmieniać! Jedyne co trzeba zmienić, to domenę, część wspólną, która bazuje bezpośrednio na kodzie aplikacji. To jednak jest o niebo łatwiejsze niż zmiana 500 testów.

Czy to już wszsystko ?

Tak, to już wszystko. Celem niniejszego wpisu nie było pokazanie gotowych rozwiązań i ich implementacji krok po kroku. Celem było przekonanie do idei rozdzielenia testów na dwie warstwy: biznesową i tą związaną bezpośrednio z implementacją. Warto też zauważyć, że choć takie rozwiązanie nie jest często stosowane przy okazji testów, to jednak nie jest niczym nowym. Moim zdaniem warto je stosować, a już na pewno warto w połączeniu Selenium + Java.

You May Also Like

How to automate tests with Groovy 2.0, Spock and Gradle

This is the launch of the 1st blog in my life, so cheers and have a nice reading!

y u no test?

Couple of years ago I wasn't a big fan of unit testing. It was obvious to me that well prepared unit tests are crucial though. I didn't known why exactly crucial yet then. I just felt they are important. My disliking to write automation tests was mostly related to the effort necessary to prepare them. Also a spaghetti code was easily spotted in test sources.

Some goodies at hand

Now I know! Test are crucial to get a better design and a confidence. Confidence to improve without a hesitation. Moreover, now I have the tool to make test automation easy as Sunday morning... I'm talking about the Spock Framework. If you got here probably already know what the Spock is, so I won't introduce it. Enough to say that Spock is an awesome unit testing tool which, thanks to Groovy AST Transformation, simplifies creation of tests greatly.

An obstacle

The point is, since a new major version of Groovy has been released (2.0), there is no matching version of Spock available yet.

What now?

Well, in a matter of fact there is such a version. It's still under development though. It can be obtained from this Maven repository. We can of course use the Maven to build a project and run tests. But why not to go even more "groovy" way? XML is not for humans, is it? Lets use Gradle.

The build file

Update: at the end of the post is updated version of the build file.
apply plugin: 'groovy'
apply plugin: 'idea'

def langLevel = 1.7

sourceCompatibility = langLevel
targetCompatibility = langLevel

group = 'com.tamashumi.example.testwithspock'
version = '0.1'

repositories {
mavenLocal()
mavenCentral()
maven { url 'http://oss.sonatype.org/content/repositories/snapshots/' }
}

dependencies {
groovy 'org.codehaus.groovy:groovy-all:2.0.1'
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0-SNAPSHOT'
}

idea {
project {
jdkName = langLevel
languageLevel = langLevel
}
}
As you can see the build.gradle file is almost self-explanatory. Groovy plugin is applied to compile groovy code. It needs groovy-all.jar - declared in version 2.0 at dependencies block just next to Spock in version 0.7. What's most important, mentioned Maven repository URL is added at repositories block.

Project structure and execution

Gradle's default project directory structure is similar to Maven's one. Unfortunately there is no 'create project' task and you have to create it by hand. It's not a big obstacle though. The structure you will create will more or less look as follows:
<project root>

├── build.gradle
└── src
├── main
│ ├── groovy
└── test
└── groovy
To build a project now you can type command gradle build or gradle test to only run tests.

How about Java?

You can test native Java code with Spock. Just add src/main/java directory and a following line to the build.gradle:
apply plugin: 'java'
This way if you don't want or just can't deploy Groovy compiled stuff into your production JVM for any reason, still whole goodness of testing with Spock and Groovy is at your hand.

A silly-simple example

Just to show that it works, here you go with a basic example.

Java simple example class:

public class SimpleJavaClass {

public int sumAll(int... args) {

int sum = 0;

for (int arg : args){
sum += arg;
}

return sum;
}
}

Groovy simple example class:

class SimpleGroovyClass {

String concatenateAll(char separator, String... args) {

args.join(separator as String)
}
}

The test, uhm... I mean the Specification:

class JustASpecification extends Specification {

@Unroll('Sums integers #integers into: #expectedResult')
def "Can sum different amount of integers"() {

given:
def instance = new SimpleJavaClass()

when:
def result = instance.sumAll(* integers)

then:
result == expectedResult

where:
expectedResult | integers
11 | [3, 3, 5]
8 | [3, 5]
254 | [2, 4, 8, 16, 32, 64, 128]
22 | [7, 5, 6, 2, 2]
}

@Unroll('Concatenates strings #strings with separator "#separator" into: #expectedResult')
def "Can concatenate different amount of integers with a specified separator"() {

given:
def instance = new SimpleGroovyClass()

when:
def result = instance.concatenateAll(separator, * strings)

then:
result == expectedResult

where:
expectedResult | separator | strings
'Whasup dude?' | ' ' as char | ['Whasup', 'dude?']
'2012/09/15' | '/' as char | ['2012', '09', '15']
'nice-to-meet-you' | '-' as char | ['nice', 'to', 'meet', 'you']
}
}
To run tests with Gradle simply execute command gradle test. Test reports can be found at <project root>/build/reports/tests/index.html and look kind a like this.


Please note that, thanks to @Unroll annotation, test is executed once per each parameters row in the 'table' at specification's where: block. This isn't a Java label, but a AST transformation magic.

IDE integration

Gradle's plugin for Iintellij Idea

I've added also Intellij Idea plugin for IDE project generation and some configuration for it (IDE's JDK name). To generate Idea's project files just run command: gradle idea There are available Eclipse and Netbeans plugins too, however I haven't tested them. Idea's one works well.

Intellij Idea's plugins for Gradle

Idea itself has a light Gradle support built-in on its own. To not get confused: Gradle has plugin for Idea and Idea has plugin for Gradle. To get even more 'pluginated', there is also JetGradle plugin within Idea. However I haven't found good reason for it's existence - well, maybe excluding one. It shows dependency tree. There is a bug though - JetGradle work's fine only for lang level 1.6. Strangely all the plugins together do not conflict each other. They even give complementary, quite useful tool set.

Running tests under IDE

Jest to add something sweet this is how Specification looks when run with jUnit  runner under Intellij Idea (right mouse button on JustASpecification class or whole folder of specification extending classes and select "Run ...". You'll see a nice view like this.

Building web application

If you need to build Java web application and bundle it as war archive just add plugin by typing the line
apply plugin: 'war'
in the build.gradle file and create a directory src/main/webapp.

Want to know more?

If you haven't heard about Spock or Gradle before or just curious, check the following links:

What next?

The last thing left is to write the real production code you are about to test. No matter will it be Groovy or Java, I leave this to your need and invention. Of course, you are welcome to post a comments here. I'll answer or even write some more posts about the subject.

Important update

Spock version 0.7 has been released, so the above build file doesn't work anymore. It's easy to fix it though. Just remove last dash and a word SNAPSHOT from Spock dependency declaration. Other important thing is that now spock-core depends on groovy-all-2.0.5, so to avoid dependency conflict groovy dependency should be changed from version 2.0.1 to 2.0.5.
Besides oss.sonata.org snapshots maven repository can be removed. No obstacles any more and the build file now looks as follows:
apply plugin: 'groovy'
apply plugin: 'idea'

def langLevel = 1.7

sourceCompatibility = langLevel
targetCompatibility = langLevel

group = 'com.tamashumi.example.testwithspock'
version = '0.1'

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
groovy 'org.codehaus.groovy:groovy-all:2.0.5'
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
}

idea {
project {
jdkName = langLevel
languageLevel = langLevel
}
}

Spock basics

Spock (homepage) is like its authors say 'testing and specification framework'. Spock combines very elegant and natural syntax with the powerful capabilities. And what is most important it is easy to use.

One note at the very beginning: I assume that you are already familiar with principles of Test Driven Development and you know how to use testing framework like for example JUnit.

So how can I start?


Writing spock specifications is very easy. We need basic configuration of Spock and Groovy dependencies (if you are using mavenized project with Eclipse look to my previous post: Spock, Java and Maven). Once we have everything set up and running smooth we can write our first specs (spec or specification is equivalent for test class in other frameworks like JUnit of TestNG).

What is great with Spock is fact that we can use it to test both Groovy projects and pure Java projects or even mixed projects.


Let's go!


Every spec class must inherit from spock.lang.Specification class. Only then test runner will recognize it as test class and start tests. We will write few specs for this simple class: User class and few tests not connected with this particular class.

We start with defining our class:
import spock.lang.*

class UserSpec extends Specification {

}
Now we can proceed to defining test fixtures and test methods.

All activites we want to perform before each test method, are to be put in def setup() {...} method and everything we want to be run after each test should be put in def cleanup() {...} method (they are equivalents for JUnit methods with @Before and @After annotations).

It can look like this:
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() {

}
}

What is more readable or preferred? It is just a matter of taste because according to Spock docs behaviour is the same in these two cases.

It is worth mentioning that JUnit @BeforeClass/@AfterClass are also present in Spock as def setupSpec() {...} and def cleanupSpec() {...}. They will be runned before first test and after last test method.


First tests


In Spock every method in specification class, expect setup/cleanup, is treated by runner as a test method (unless you annotate it with @Ignore).

Very interesting feature of Spock and Groovy is ability to name methods with full sentences just like regular strings:
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
In next section I am going to describe each block shortly with little examples.

given block

This block is used to setup test objects and their state. It has to be first block in test and cannot be repeated. Below is little example how can it be used:
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

When block contains action we want to test (Spock documentation calls it 'stimulus'). This block always occurs in pair with then block, where we are verifying response for satisfying certain conditions. Assume we have this simple test case:
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

Expect block is primarily used when we do not want to separate when and then blocks because it is unnatural. It is especially useful for simple test (and according to TDD rules all test should be simple and short) with only one condition to check, like in this example (it is simple but should show the idea):
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

These two blocks have the very same functionality as the def setup and def cleanup methods in specification. They allow to perform some actions before test and after test. But unlike these methods (which are shared between all tests) blocks work only in methods they are defined in. 


where - easy way to create readable parameterized tests

Very often when we create unit tests there is a need to "feed" them with sample data to test various cases and border values. With Spock this task is very easy and straighforward. To provide test data to feature method, we need to use where block. Let's take a look at little the piece of code:

def "should successfully validate emails with valid syntax"() {
expect:
emailValidator.validate(email) == true
where:
email }

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 result }
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?


Well, that was just quick and short journey  through Spock and its capabilities. However, with that basic tutorial you are ready to write many unit tests. In one of my future posts I am going to describe more features of Spock focusing especially on its mocking abilities.