We (the cucumber team) are missing to set a convention for a directory structure on JVM projects that are using cucumber.
I have for many years used cucumber for a ruby-on-rails (and also some ruby-non-rails) projects.
I have get used to the directory structure:
my-project
└── features
├── *.feature
├── step_definitions
│ └── *_steps.rb
└── support
└── env.rb
I am now working on a Groovy and Grails project. So we are using the
cucumber-jvm
version to which there is a package for each jvm
language. So we are using info.cukes:cucumber-groovy:1.2.4
package.
I am in a clean up phase and is therefore looking for a convention for using cucumber on a groovy and grails project. For those of you who don't know it the convention in grails projects is to use gradle as the build tool.
The grails framework is a convention-over-configuration framework, just like RubyOnRails. However there seems to be no one convention using cucumber in a Grails project (or any other cucumber-JVM based project).
Gradle (and therefore also Grails) projects (and maven and in generally JVM projects) have a convention on directory base structure like this
my-project
└── src
├── main
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
└── test
├── groovy
│ └── <package structure>
├── java
│ └── <package structure>
└── resources (directory)
The groovy
and java
directories may not exists if there are no
files in that language.
Grails have extended this convention with integration-test
(to
seperate integration tests from unit test), like this:
my-project
└── src
├── integration-test
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
├── main
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
└── test
├── groovy
│ └── <package structure>
├── java
│ └── <package structure>
└── resources (directory)
So far I have found up to 3 conventions to use cucumber-jvm:
- The cucumber for java book.
- Official skeleton and examples.
- The defaults from the gradle plugin: https://github.com/samueltbrown/gradle-cucumber-plugin
This is not taking in consideration the default value for directory
arguments (such as --glue
) or default final argument (the directory
with feature files) of the cucumber CLI tool.
The book basically does not make a stance on this subject at all, which I by itself find sad because that was one of the few things that I had hoped to find in this book. However the examples (from Chapter 2, First taste and Chapter 14, Controlling cucumber)
my-project
├── features
│ └── *.feature
└── step_definitions (this directory structure is also the package structure)
├── hooks
│ └── *Hooks.java
├── widgets
│ └── *Steps.java
└── other_sub_package
└── *Steps.java
So the glue code on relies in the JVM package
step_definitions
. Notice that these examples differ from the
standard packaging convention in the JVM world. The convention on
packaging standard is to use organisational domain prefix like
dk.softace.my-project.*
(or at least my-project
). Note also that
step definitions and hooks lives in the same package, which is not
optimal since step definitions may very well be usable across
projects, where as hooks is usually very specific to the project in
hand and not reusable.
- The official java skeleton: https://github.com/cucumber/cucumber-java-skeleton
- The groovy example: https://github.com/dkowis/cucumber-jvm-groovy-example
Both the (official) Java skeleton and groovy example follows the JVM
project directory standard as sketched out above. The project is in
the package skeleton
, and calc
respectively, not quite following
convention like info.cukes.skeleton
or similar.
In the Java skeleton, the files (and classes) containing the step
definitions are named Stepdefs
, not *Steps
as would be more
natural.
In the Groovy example the glue file/class (CalculatorSteps
) contains
all three types of glue; step definitions (Given
, etc) , hooks
(Before
and After
), and environment setup (World
)
Disclaimer: This section may not be perfectly clear nor correct. The project is pre-1.0 release and is under development, the documentation differ a bit from the implementation and it is not clear which one is ahead of the other.
The gradle plugin is true to the convention in the gradle world and it
therefore proposes a directory as an extension to the standard JVM
project structure by introducing cucumber
along side main
and
test
(and integrations-test
in case of Grails project). This makes sense (to me) because glue code is not test code by itself, it is ... glue code. The tests are feature
files.
So the directory structure looks like this:
my-project
└── src
├── cucumber
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
│ └── *.feature
├── main
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
└── test
├── groovy
│ └── <package structure>
├── java
│ └── <package structure>
└── resources (directory)
The documentation says that if you do not configure this, the
default behaviour is to look under src/test/resources
for features and
src/test/java
for glue code. I think it is because it falls back the
the cucumber-jvm defaults.
The plugin also only mentions using the groovy package
info.cukes:cucumber-groovy
, but I think it will also make sense
(work) using another cucumber-JVM package. However it is a Gradle plugin.
I will here come with a (incomplete) proposal for an official convention using cucumber for JVM projects. It is only a proposal and I hope pros and cons will be discussed openly.
Whatever becomes the output of this discussion I hope we (the cucumber team) will
- Implement this convention as default directory values in all JVM packages.
- Update documentation to emphasize the convention.
- Update all known examples to follow this convention.
I am eager to put a descent amount of effort into updating documentation and examples. I am also willing to work on updating the implementation, however I have no development history on the cucumber-jvm implementation, so others may overtake me there.
I think the gradle plugin has made a good decision to introduce the
cucumber
section in a standard JVM project structure and I suggest
that we elevate that to official cucumber convention. In addition to
that I suggest that we make a convention for glue code packages and
distinguish between
- step definitions
*Steps.java
and*Steps.groovy
containingGiven
,When
, etc.
- hooks
*Hooks.java
and*Hooks.groovy
containingBefore
,After
, etc.
- environment setup (known as env.rb in ruby)
env.groovy
orenv.java
containing miscelanous execution setup, hereamongWorld
, etc.
So a dir layout would look like this (for groovy):
my-project
└── src
├── cucumber
│ ├── groovy
│ │ └── step_definitions
│ │ │ └── *Steps.groovy
│ │ ├── hooks.groovy
│ │ └── support
│ │ └── env.groovy
│ └── resources (directory)
│ └── *.feature
├── main
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
└── test
├── groovy
│ └── <package structure>
├── java
│ └── <package structure>
└── resources (directory)
Here is a quite drastic alternative. Cucumber tests are written in Gherkin, they are tests (source code written in Gherkin), not resources. Having that in mind the should be put a directory called gherkin
, like this:
my-project
└── src
├── cucumber
│ ├── groovy
│ │ └── step_definitions
│ │ │ └── *Steps.groovy
│ │ ├── hooks.groovy
│ │ └── support
│ │ └── env.groovy
│ ├── gherkin
│ │ └── *.feature
│ └── resources
│ └── <static fixture files>
├── main
│ ├── groovy
│ │ └── <package structure>
│ ├── java
│ │ └── <package structure>
│ └── resources (directory)
└── test
├── groovy
│ └── <package structure>
├── java
│ └── <package structure>
└── resources (directory)
I would define a feature file as a SPEC. (it's an executable specification) as that is the primary purpose, driving the shared understanding of how the product works. Yes it is parsed and used to drive tests that validate the product is behaving as expected, but the 'test code' per say is all in the step files and any helper methods etc that the steps call.