Plugin Guide

Martin Hauner edited this page Jan 28, 2015 · 16 revisions

Cucumber Grails Plugin Guide

Content

Introduction

grails-cucumber is a Grails plugin for Cucumber based on Cucumber-JVM. Cucumber-JVM is the JVM implementation of cucumber and it supports many JVM languages. Groovy is one of them.

grails-cucumber is a functional test plugin for grails. It is not meant to implement tests on the unit test level. It is only available in the functional test phase.

The plugin runs cucumber inside a running grails application which allows us to use the grails api in the cucumber step implementations. We can call GORM stuff like dynamic finders, services or even controllers.

since 0.10.0, Grails 2.3

Note Running functional tests and the app in the same process is deprecated by Grails. It (currently) does still work but it is not recommended to avoid subtle differences between test and live environment. Therefore the in-process features of the plugin are deprecated too.

Plugin Installation

grails-cucumber is a normal grails plugin. To use it in your grails application just add it to the plugins configuration block in BuildConfig.groovy like this (using the latest available version of the plugin):

plugins {
   test ":cucumber:0.7.0"
}

.feature Files Location

The only convention regarding cucumber .feature files is that they should be in the test/functional directory. Inside it you can use any layout you like to structure the features.

The plugin will tell Cucumber-JVM to look for features in this directory and Cucumber-JVM will scan it recursivly.

If you need to you can override this path. See Plugin Configuration.

Step Implementations Location

The step implementations follow the same convention as the .features. They should be in test/functional and inside it you can use any layout you like to structure the steps.

The plugin will tell Cucumber-JVM to look for the steps in this directory and Cucumber-JVM will scan it recursivly.

Note that you should not mix cucumber groovy scripts with other groovy scripts. Cucumber-JVM will run all groovy scripts it will find in the given path. Chances are high that this will fail and is probably not what you want anyway.

If you need to you can override this path. See Plugin Configuration.

Compiled Steps

since 0.9.0
It is possible to compile the step files before running the features. There are two things to configure in CucumberConfig.groovy (see configuration): first, the location of the source files using the sources option and second, the classpath of the steps using the glue option:

    cucumber {
        // steps, hooks etc that will be compiled
        sources = ["test/cucumber"]

        // .. and where cucumber will find the compiled steps & hooks
        glue = ["classpath:<the steps and hooks package>"]
    }

A layout that seperates the feature files from the step code could look like this:

    cucumber {
        // here we save the feature files...
        features = ["test/cucumber"]

        // steps, hooks etc that will be compiled
        // if the steps are in "test/functional" we do not need to configure it
        // sources = ["test/functional"]

        // .. and where cucumber will find the compiled steps & hooks
        glue = ["classpath:<the steps and hooks package>"]
    }

Note

The steps should be in their own package and there should be no other (normal) classes in this package. Cucumber will load all classes in the given package(s). To avoid complications it is recommended to keep the steps in isolation. It is ok to put (normal) classes into the sources directories to compile them but they should be in a different package that is not listed in the glue configuration.

See Plugin Configuration for the details of the source and glue option.

Step Implementations

This section does contain some basic information for implementing steps: What is possible at all and how do I use specific grails functionality.

calling GORM

deprecated since 0.10.0

Grails 2.3+ Note: this will not work in forked mode but only in non forked mode.

There is nothing special about using GORM, just use it as in production code. Having a simple domain class Book

package books

class Book {
    String author
    String title
}

we can call dynamic finders as usual:

When (~"^I add \"([^\"]*)\"\$") { String bookTitle ->
   def book = Book.findByTitle (bookTitle)
   ...
}

calling Services

deprecated since 0.10.0

Grails 2.3+ Note: this will not work in forked mode but only in non forked mode.

To create a Service in a cucumber step call appCtx.getBean ("<bean name>") like this:

Given (~"^I have already added \"([^\"]*)\"\$") { String bookTitle ->
    def bookService = appCtx.getBean ("bookService")
    // do something with your service
}

calling Controllers

deprecated since 0.10.0

Grails 2.3+ Note: this will not work in forked mode but only in non forked mode.

We can create & use Controllers in the same way as in a normal grails controller integration test. We just have to add some setup & cleanup that we get automatically when we implement normal integration tests by extending GroovyTestCase. That is, setting up the mock request , response and session objects.

It is just a few lines you have to call from from the cucumber Before and After hooks.

since 0. 6.0
Alternatively to adding the two hooks manually we can also add a simple configuration block into a support file (like env.groovy) which will create the two hooks.

import static grails.plugin.cucumber.Hooks.hooks

hooks {
    request ("@req")
}

The request setting takes a cucumber tag expression as parameter and adds a Before & After hook passing the tag expression ("@req" in the above example) to the hooks (so they will run only for features or scenarios marked with the @req tag).

The next section shows the code that is executed in the request hooks. See also Standard Hooks in Options.

Before & After Code

Here is the required code to put in the Before and After hooks to get controller support in the step implementations:

import org.codehaus.groovy.grails.test.support.GrailsTestRequestEnvironmentInterceptor

this.metaClass.mixin (cucumber.runtime.groovy.Hooks)


GrailsTestRequestEnvironmentInterceptor scenarioInterceptor

Before () {
    scenarioInterceptor = new GrailsTestRequestEnvironmentInterceptor (appCtx)
    scenarioInterceptor.init ()
}

After () {
    scenarioInterceptor.destroy ()
}

calling Geb

See Testing-Grails-with-Cucumber-and-Geb for a complete tutorial.

Before & After Code

Transactions

Apart from a single constellation the plugin does/can not provide any support to wrap a scenario into a transaction to automate the cleanup of the database. We will have to provide Before and After hooks to setup and cleanup the database (see cucumber & Grails: Transaction Rollback). There are two options:

  • re-create the database schema on each scenario
  • restore the original database state with update/delete statements

Running Cucumber Features

The plugin integrates cucumber as a grails test type into the grails test infrastructure. You can run the cucumber features via the grails test-app command and the test results will be included in the normal grails test reports.

Currently the plugin registers the cucumber test type only to the functional test phase. To run the cucumber features you call grails by one of the following commands:

grails test-app functional:cucumber
grails test-app :cucumber

Or, if you do not use a second functional test plugin you can also use:

grails test-app functional:

Command Line Options

@tag filter

since 0.6.0 If the command line contains :cucumber, @tag arguments are evaluated to filter execution of features or scenarios. Standard cucumber syntax applies without the --tags option keyword. Setting tags on the comand line will overwrite CucumberConfig.groovy:

grails test-app :cucumber @foo,~@bar @zap

file/dir filter

since 0.8.0 If the command line contains :cucumber and some other arguments (i.e no @tags) they are evaluated as file/dir and line filter for features and scenarios. The filter info is just passed through to cucumber and can be whatever cucumber does except. Here are a few examples:

// features
grails test-app :cucumber foo.feature bar.feature

// feature with line numbers
grails test-app :cucumber foo.feature:10:17

// feature with "full" path
grails test-app :cucumber test/functional/foo/bar.feature

// feature directories
grails test-app :cucumber foodir bardir

Normally you would have to provide the full path (relative to the grails project root directory) to the files/dirs. As a convinience, the given feature files/dirs will be prefixed with the configured feature path (see Options).

If the feature path is not changed, all files/dirs will be prefixed with test/functional. Calling

grails test-app :cucumber foo.feature

the plugin will pass test/functional/foo.feature to cucumber.

Note that this step is skipped if you have configured multiple feature paths. Because the plugin can not decide which feature path contains a given file/dir you have to provide the full path yourself.

--plugin ( --format)

since 1.1.0, this option has been renamed (cucumber-jvm 1.2.0), its old name will still work
(since 0.10.0)

Using the --plugin arg on the command line will add an additional plugin. The plugin description will be passed "as is" to cucumber and has the same syntax as cucumbers --plugin option:

// use json plugin
grails test-app :cucumber --plugin=json:target/test-reports/cucumber.json

Note that there is a '=' character between --plugin and its value and that only one --plugin option can be used. Multiple plugins can be configured in CucumberConfig.groovy (see Options).

Test Reports

In JUnit we have test suites and tests. In Cucumber we have features, scenarios and steps. The plugin maps features to test suites and scenarios to tests. There is no direct mapping for steps. The steps will be reported as part of their scenario.

Grails does only distinguish between failures and errors and knows nothing about undefined steps. Undefined steps will be therefore reported as failure which makes the scenario fail too.

The plugin integrates into grails test reporting. The normal cucumber output can be found in the html or plain test reports in target/test-reports.

Plugin Configuration

Convention over Configuration

The plugin does use a couple of default settings that can be overriden by CucumberConfig.groovy if you need to. If a setting is not given in the configuration file the default will be used.

The defaults in CucumberConfig.groovy notation are:

cucumber {
    tags = []

    features = ["test/functional"]
    glue = ["test/functional"]
}

The hardcoded test/cucumber path used in the plugin before version 0.4.0 is now deprecated. If you still like to use it you can add it to the features and steps setting in CucumberConfig.groovy.

cucumber {
    features = ["test/cucumber"]
    glue = ["test/cucumber"]
}

Location

The plugin will read the configuration from grails-app/conf/CucumberConfig.groovy. It is a a standard config slurper file.

We can adjust the paths where cucumber will look for .feature files and step implementations so putting the configuration along the cucumer files does not work.

Options

Tags

Cucumber tags can be set using the tags configuration option:

cucumber {
    tags = ["@implemented", "~@ignored"]
}

tags is a list of strings where each item corresponds to a single cucumber --tags option. See the cucumber documentation to read more about the --tags switch.

.feature File Location

To override or extend the locations where cucumber will search for .feature files you can use the features option:

cucumber {
    features = [
        "test/functional/features",
        "test/functional/featuresOfCurrentSprint"
    ]
}

features is a list of paths where each path is relative (below) the grails application root directory.

Note that settings this option will override the default test/functional path. If you want to keep it you have to add it to the features path list.

Plugins (Formats/Formatters)

since 1.1.0, this option has been renamed (cucumber-jvm 1.2.0), its old name will still work (since 0.10.0)
Additional cucumber formats/formatters can be set with the plugins configuration option:

cucumber {
    plugins = [
        "json:target/test-reports/cucumber.json",
        "html:target/test-reports/cucumber"
    ]
}

plugins is a list of strings where each item corresponds to a single cucumber --plugin option.

The plugins (default) grails formatter will aways run.

Step Sources Location (compiled Steps)

since 0.9.0
To compile the steps before running the features you have to configure the location of the step source files. This is done with the source option:

cucumber {
    // steps, hooks etc that will be compiled
    sources = ["test/cucumber"]

    // remember to set the glue option!
}

It is possible to list multiple source directories.

If grails-cucumber does find a sources option in the configuration it will compile all files in the given directories. sources does not have a default value, so if not set the plugin will not use compiled steps.

If you use test/functional as the step source directory you don't have to add the source option. test/functional will be compiled by grails automatically.

To use compiled steps it it also required to configure the packages of the steps in the glue option. This is described in the next section.

Step Implementation Location

To override or extend the locations cucumber will search for groovy scripts with gherkin steps or hooks you can use the glue option:

cucumber {
    glue = [
        "test/functional/steps",
        "test/functional/hooks"
    ]
}

glue is a list of paths where each path is relative (below) the grails application root directory.

Note that settings this option will override the default test/functional path. If you want to keep it you have to add it to the glue path list.

since 0.9.0
If the steps and hooks are compiled we have to tell cucumber the package names instead of the paths to the step source files. This is done using the classpath notation:

cucumber {
    glue = [
        "classpath:<package name>",
        "classpath:<another package name>"
    ]
}

Standard Hooks

since 0.6.0, deprecated since 0.10.0

Grails 2.3+ Note: this will not work in forked mode but only in non forked mode.

The plugin provides a couple of standard hooks that can be enabled using the hooks configuration block. It should be placed in a groovy file on the glue path:

import static grails.plugin.cucumber.Hooks.hooks

hooks {
    integration ("@i9n")
}

The integration setting enables grails integration test support which we will get automatically in normal integration tests by extending GroovyTestCase. It will simply create a Before and a After hook pair with the given tag expression to set up and clean up grails integration testing. Note that this is only usefull for ui-less testing.

The following hooks are supported:

FAQ & Pitfalls

  • The plugin fails when I try to run features in a non-english language.

    If cucumber does not recognize the # language: <language code> comment at the beginning of a feature file it is probably because of a byte order marker (BOM). Remove it and try again.