Test With Spring Course
Save Time by Writing Less Test Code
  • Home
  • Pre-Sales FAQ
  • Support
  • Log In

/ July 6, 2017 / petrikainulainen

Introduction to Feature Methods

Lesson Progress:
← Back to Topic

After we have finished this lesson, we:

  • Can identify the building blocks of a feature method.
  • Can use these building blocks when we write feature methods.

This lesson assumes that:

  • You are familiar with Spock Specifications

Watch the Lesson

 

The text version of this lesson is given in the following:

The Structure of a Feature Method

A feature method describes the expected behavior of a feature that is implemented by the system under specification. We can name our feature methods by using String literals, and this is a huge advantage over JUnit 4 (or TestNG) because we can (and we should) use sentences that actually make sense.

The source code of a specification class that has one empty feature method looks as follows:

import org.junit.experimental.categories.Category
import spock.lang.Specification
 
@Category(UnitTest.class)
class ExampleSpec extends Specification {

	def 'A feature method'() {
	}
}

Every feature method consists of so-called blocks. Each block has a label, and the body of the block extends to the beginning of the next block or to the end of the feature method. A feature method might have the following blocks:

  • The setup block must be the first block of a feature method and it contains the configuration of the described feature. A feature method can have only one setup block.
  • The when and then block describes the stimulus (when) and the expected response (then). A feature method can have multiple when and then blocks.
  • The expect block describes the stimulus and the expected response in a single expression. A feature method can have only one expect block, and it is possible to add both when and then and expect blocks to the same feature method. However, this is not very practical.
  • The cleanup block is used to clean up the resources used by a feature method, and it is invoked even if the feature method throws an exception. A feature method can have only one cleanup block.
  • The where block must be the last block of a feature method, and it is used to write data-driven feature methods. A feature method can have only one where block.

This lesson helps you to use setup, when and then, expect, and cleanup blocks. We will talk more about the where block in the next lesson of this topic.


The following figure illustrates the structure of a feature method:

In other words, the structure of a feature method looks as follows:

import org.junit.experimental.categories.Category
import spock.lang.Specification
  
@Category(UnitTest.class)
class ExampleSpec extends Specification {
 
    def 'A feature method'() {
		//setup block
	    //when and then blocks
	    //expect block
	    //cleanup block
	    //where block
    }
}

Additional Reading:

  • Spock Primer: Blocks


We can now identify the blocks of a feature method. Let’s move on and find out how we can use these blocks when we write feature methods.

Writing Feature Methods

During this lesson we will learn to use these blocks when we create a feature method which specifies the expected behavior of the get() method of the HashMap class. Let’s start by finding out how we can use the setup block.

Using the Setup Block

As we remember, the setup block is the first block of a feature method and we can use it for configuring the described feature. The setup block has an optional label (setup or given) and we can create an implicit setup block by omitting that label.

Let’s create an implicit setup block that puts one value to the specified HashMap object. After we have created this setup block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {
        def key = 'key'
        def value = 1
        map.put(key, value)
    }
}

If we think that that the implicit setup block is not clear enough, we can start our setup block by using the label: setup. After we have added this label to our setup block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {
	
		setup:
        def key = 'key'
        def value = 1
        map.put(key, value)
    }
}

If we want to write our feature methods by using the given-when-then format, we can replace the setup label with the label: given and describe our setup block by using a String literal.

After we have modified our setup block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {
	
		given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1
        map.put(key, value)
    }
}

Additional Reading:

  • Spock Primer: Setup Blocks


After we have configured the described feature, we have to specify the expected behavior. Let’s find out how we can do it by using when and then blocks.

Using When and Then Blocks

We can specify the expected behavior of the described feature by using when and then blocks which always occur together. The when block describes the stimulus and the then block describes the expected response.

When we want to use when and then blocks, we have to follow these steps:

First, we have to create a when block by following these rules:

  • A when block must start with the label: when.
  • A when block can have an optional description that is given by using a String literal.
  • A when block can contain any code.

Second, we have to create a then block by following these rules:

  • A then block must be placed right after the when block that describes the stimulus.
  • A then block must start with the label: then.
  • A then block can have an optional description that is given by using a String literal.
  • A then block can contain only variable definitions, conditions, exception conditions and interactions.

Let’s create a when and then block which verifies that the get() method of the HashMap class returns the found value. After we have created this block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapWhenThenSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1
        map.put(key, value)

        when: 'A value is found with the given key'
        def found = map.get(key)

        then: 'Should return the found value'
        found == value
    }
}

As we remember, a feature method can have multiple when and then blocks. Let’s create another when and then block which verifies that the get() method of the HashMap class returns null if no value is found with the given key.

After we have created this block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapWhenThenSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1
        map.put(key, value)

        when: 'A value is found with the given key'
        def found = map.get(key)

        then: 'Should return the found value'
        found == value

        when: 'A value is not found with the given key'
        def incorrectKey = 'incorrectKey'
        found = map.get(incorrectKey)

        then: 'Should return null'
        found == null
    }
}

Additional Reading:

  • Spock Primer: When and Then Blocks

When we take a closer look at our feature method, we notice that the when block seems a bit artificial. Let’s clean up our feature method by using the expect block.

Using the Expect Block

An expect block describes the stimulus and the expected response in a single expression. We can create an expect block by following these rules:

  • An expect block must start with the label: expect.
  • An expect block can have an optional description that is given by using a String literal.
  • An expect block can contain only conditions and variable definitions.

Let’s create an expect block which verifies that the get() method of the HashMap class returns the found value. After we have created this block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapExpectSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1
        map.put(key, value)

        expect: 'Should return the found value'
        map.get(key) == value
    }
}

Our next step is to verify that the get() method of the HashMap class returns null when no value is found with the given key. Because a feature method can have only one expect block, we can “divide” that block into several parts by using the and label. The and label is used to describe individual parts of a block, and it has an optional description that is given by using a String literal.

After we have added a new part to the expect block, the source code of our specification class looks as follows:

@Category(UnitTest.class)
class HashMapExpectSpec extends Specification{

    def map = new HashMap()

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1
        map.put(key, value)

        expect: 'Should return the found value'
        map.get(key) == value

        and: 'Should return null when value is not found'
        def incorrectKey = 'incorrectKey'
        map.get(incorrectKey) == null
    }
}

It should be clear that we can specify the expected behavior by using either a when and then or an expect block. The question is: how can we choose the right tool for the job?

The Spock documentation helps us to find an answer to this question:

As a guideline, use when-then to describe methods with side effects, and expect to describe purely functional methods.


Additional Reading:

  • Spock Primer: Expect Blocks


We can now specify the expected behavior of the described feature by using both when and then and expect blocks. This is a good start, but sometimes our feature method reserves resources that must be freed afterwards.

Let’s find out how we can clean up these resources by using the cleanup block.

Using the Cleanup Block

A cleanup block is used to free the resources used by a feature method, and Spock guarantees that it is invoked even if the feature method throws an exception. We can create a cleanup block by following these rules:

  • A cleanup block must start with the label: cleanup.
  • A cleanup block must be placed after the when and then and/or expect blocks.

Let’s assume that we have created a feature method which creates a new file. Naturally we want to delete the created file after the feature method has been finished successfully or after an exception has been thrown.

We can do this by adding the following cleanup block to our feature method:

@Category(UnitTest.class)
class FileSpec extends Specification {

    def 'Create a new file'() {

        setup:
        def file = new File("/tmp/foo.txt")

        when: 'A new file is created'
        file.createNewFile()

        then: 'Should create a new file'
        file.exists()
        file.isFile()
        !file.isDirectory()

        cleanup:
        file?.delete()
    }
}

Because the cleanup block is invoked even if the feature method throws an exception, we must write our cleanup block by using defensive programming because we cannot know which line throws the exception.

Additional Reading:

  • Spock Primer: Cleanup Blocks


We can now identify the blocks of a feature method, and we know how we can use these blocks when we write feature methods.

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us six things:

  • A feature method consists of blocks.
  • The setup block configures the described feature.
  • The when and then block describes the stimulus and the expected response.
  • The expect block describes the stimulus and the expected response in a single expression.
  • The cleanup block is used to clean up the resources used by a feature method.
  • We can describe individual parts of a block by using the and label.

Get the source code from Github

← Previous Lesson Next Lesson →

Can I help you?

This is a free sample lesson of my Test With Spring course. If this lesson helped you to solve your problem, you should find out how my testing course can help you.

Support and Privacy

  • Pre-Sales FAQ
  • Support
  • Cookie Policy
  • No Bullshit Privacy Policy
  • No Bullshit Terms and Conditions

Test With Spring Course

  • Starter Package
  • Intermediate Package
  • Master Package

Free Sample Lessons

  • Introduction to JUnit 4
  • Introduction to Unit Testing
  • Introduction to Integration Testing
  • Introduction to End-to-End Testing
  • Introduction to Spock Framework
  • Introduction to Integration Testing – Spock Edition
  • Writing End-to-End Tests With Spock Framework

Copyright Koodikupla Oy 2016 — Built on Thesis by Themedy

This website collects some information from you. The collected information is described on the privacy policy.CloseRead Privacy Policy