Grails: grails-cucumber and compiled steps

I am finally finishing the precompiled steps support in the cucumber grails plugin.

Currently if we run grails test-app functional:cucumber it will fire up the whole application and then run cucumber. Cucumber will load, parse and execute all our groovy step source files. If there is an import missing in one of the source files (we will look at an example in a minute) it will throw an error at us. At runtime.

Compiling the cucumber glue code has the advantage that we can catch this already at compile time before grails runs the application. Depending on the size of your app this can save you a number of time consuming round trips.

Let’s take the grails-cucumber Book example and remove the BookController import from BookSteps.groovy.

//disabled the import below...
//import books.BookController
import data.Data

import static cucumber.api.groovy.EN.*

...
When (~'^I add "([^"]*)"$') { String bookTitle ->
    bookController = new BookController ()
    bookController.params << Data.findByTitle (bookTitle)
    bookController.add ()
}
...

The When step create a new BookController. Now if we run it with the current version of grails-cucumber (0.8.0) we will get the following output:

Prompt:Books hauner$ grails test-app functional:cucumber
| Environment set to test.....
| Packaging Grails application..
| Packaging Grails application.....
| Server running. Browse to http://localhost:8080/Books
| Compiling 2 source files.
| Error Compilation error compiling [cucumber] tests: startup failed:
/Users/hauner/Development/Grails/grails-cucumber.git/test/projects/Books/test/functional/steps/BookSteps.groovy: 12: unable to resolve class BookController 
 @ line 12, column 22.
       bookController = new BookController ()
                        ^

/Users/hauner/Development/Grails/grails-cucumber.git/test/projects/Books/test/functional/steps/BookSteps.groovy: 33: unable to resolve class BookController 
 @ line 33, column 22.
       bookController = new BookController ()
                        ^

2 errors
 (Use --stacktrace to see the full trace)

We can see that grails is packaging the application and starts the server, then cucumber starts loading the step code and fails as expected.

Now using the new precompiled steps feature it will look like this:

Prompt:Books_compile hauner$ grails test-app functional:cucumber
| Environment set to test.....
| Compiling 1 source files.
| Error Compilation error compiling cucumber glue code:
            startup failed:
/Users/hauner/Development/Grails/grails-cucumber.git/test/projects/Books_compile/test/cucumber/steps/BookSteps.groovy: 14: unable to resolve class BookController 
 @ line 14, column 22.
       bookController = new BookController ()
                        ^

/Users/hauner/Development/Grails/grails-cucumber.git/test/projects/Books_compile/test/cucumber/steps/BookSteps.groovy: 35: unable to resolve class BookController 
 @ line 35, column 22.
       bookController = new BookController ()
                        ^

2 errors
 (Use --stacktrace to see the full trace)

No packaging and no server startup before it fails. Cool ;-)

To use precompiled steps we only need to modify the plugin configuration a little bit. To make this more interesting I have also moved the step code to another directory to separate the feature description from the implementation.

The default layout I use in the Book examples has features and step code in the functional folder.

test
  \-- functional
        \-- data
              Data.groovy 
        \-- hooks
              env.groovy
        \-- steps
              BookSteps.groovy
        ListBooks.feature
        NewBook.feature

I changed the layout for the new Book_compile example to:

test
  \-- cucumber
        \-- data
              Data.groovy 
        \-- hooks
              env.groovy
        \-- steps
              BookSteps.groovy
  \-- functional
        ListBooks.feature
        NewBook.feature

I choose cucumber for the source files but it can by anything you like. Apart from the source directories we have to tell cucumber were it will find the (compiled) steps.

This will look like this (CucumberConfig.groovy):

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

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

With the (new) sources directive we set the source directories and with the (existing) glue directive we tell cucumber where it will find the step code in the classpath by prefix the entries with classpath:.

In my original example the cucumber source files do not have a package name. This is bad for compiling the steps because we would have to tell cucumber to start searching for them at the default package. This is not a good idea because cucumber would check all .class files in the whole classpath to find the glue code.

It is better to properly package the step code so we can make sure cucumber will only look at classes it cares about.

In the example I have used multiple top level packages to show that it is possible. Usually you should place it under the same parent package so that there is only a single classpath: required for the configuration.

Note that we did not add the data path to glue. It is just a helper class that does not contain cucumber code, so we do not need to tell cucumber about it. If we add it, cucumber will ignore it.

test/functional

if you want to keep the source below test/functional that is ok too. You do not need to set it as sources in CucumberConfig.groovy but you still have to adjust the glue settings.

that’s it…

I hope you will like this new feature of grails-cucumber. I still have some cleanup todo before I will release it, but it is on its way.. :-)

Advertisements

2 thoughts on “Grails: grails-cucumber and compiled steps

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s