Type Registry
package com.example;
import io.cucumber.java.DataTableType;
import io.cucumber.java.en.Given;
import java.util.List;
import java.util.Map;
public class StepDefinitions {
@DataTableType
public Author authorEntry(Map<String, String> entry) {
return new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook"));
}
@Given("There are my favorite authors")
public void these_are_my_favourite_authors(List<Author> authors) {
// step implementation
}
}
package com.example
import io.cucumber.java.DataTableType
import io.cucumber.java.en.Given
import kotlin.streams.toList
class StepDefinitions {
@DataTableType
fun authorEntry(entry: Map<String, String>): Author {
return Author(
entry["firstName"],
entry["lastName"],
entry["famousBook"])
}
@Given("There are my favorite authors")
fun these_are_my_favourite_authors(authors: List<Author>) {
// step implementation
}
}
package com.example
import io.cucumber.scala.{ScalaDsl, EN}
class StepDefinitions extends ScalaDsl with EN {
DataTableType { entry: Map[String, String] =>
Author(
entry("firstName"),
entry("lastName"),
entry("famousBook"))
}
Given("There are my favorite authors") { authors: List[Author] =>
// step implementation
}
}
The parameter type example:
The parameter type example:
The parameter type example:
package com.example;
import io.cucumber.java.ParameterType;
import io.cucumber.java.en.Given;
public class StepDefinitions {
@ParameterType(".*")
public Book book(String bookName) {
return new Book(bookName);
}
@Given("{book} is my favorite book")
public void this_is_my_favorite_book(Book book) {
// step implementation
}
}
package com.example
import io.cucumber.java.ParameterType
import io.cucumber.java.en.Given
class StepDefinitions {
@ParameterType(".*")
fun book(bookName: String): Book {
return Book(bookName)
}
@Given("{book} is my favorite book")
fun this_is_my_favorite_book(book: Book) {
// step implementation
}
}
package com.example
import io.cucumber.scala.{ScalaDsl, EN}
class StepDefinitions extends ScalaDsl with EN {
ParameterType("book", ".*") { bookName: String =>
Book(bookName)
}
Given("{book} is my favorite book") { book: Book =>
// step implementation
}
}
The docstring type example:
The docstring type example:
The docstring type example:
package com.example;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DocStringType;
import io.cucumber.java.en.Given;
public class StepsDefinitions {
private static ObjectMapper objectMapper = new ObjectMapper();
@DocStringType
public JsonNode json(String docString) throws JsonProcessingException {
return objectMapper.readValue(docString, JsonNode.class);
}
@Given("Books are defined by json")
public void books_are_defined_by_json(JsonNode books) {
// step implementation
}
}
package com.example
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import io.cucumber.java.DocStringType
import io.cucumber.java.en.Given
class StepsDefinitions {
companion object {
private val objectMapper = ObjectMapper()
}
@DocStringType
@Throws(JsonProcessingException::class)
fun json(docString: String): JsonNode {
return objectMapper.readValue(docString, JsonNode::class)
}
@Given("Books are defined by json")
fun books_are_defined_by_json(books: JsonNode) {
// step implementation
}
}
package com.example
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper
import io.cucumber.scala.{ScalaDsl, EN}
object StepsDefinitions {
private val objectMapper = ObjectMapper()
}
class StepsDefinitions extends ScalaDsl with EN {
DocStringType("json") { docString: String =>
objectMapper.readValue(docString, classOf[JsonNode])
}
Given("Books are defined by json") { books: JsonNode =>
// step implementation
}
}
For lambda defined step definitions, there are DataTableType
, ParameterType
and DocStringType
functions:
For lambda defined step definitions, there are DataTableType
, ParameterType
and DocStringType
functions:
package com.example;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java8.En;
import java.util.Map;
public class LambdaStepDefinitions implements En {
private static ObjectMapper objectMapper = new ObjectMapper();
public LambdaStepDefinitions() {
DataTableType((Map<String, String> entry) -> new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook")
));
ParameterType("book", ".*", (String bookName) -> new Book(bookName));
DocStringType("json", (String docString) ->
objectMapper.readValue(docString, JsonNode.class));
}
}
package com.example
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import io.cucumber.java8.En
class LambdaStepDefinitions : En {
init {
val objectMapper = ObjectMapper()
ParameterType("book", ".*") { s : String ->
Book(s)
}
DataTableType { entry: Map<String, String> ->
Author(entry["firstName"], entry["lastName"], entry["famousBook"])
}
DocStringType("json") { docString: String ->
objectMapper.readValue(docString, JsonNode::class)
}
}
}
package com.example;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DefaultDataTableCellTransformer;
import io.cucumber.java.DefaultDataTableEntryTransformer;
import io.cucumber.java.DefaultParameterTransformer;
import java.lang.reflect.Type;
public class StepDefinitions {
private final ObjectMapper objectMapper = new ObjectMapper();
@DefaultParameterTransformer
@DefaultDataTableEntryTransformer
@DefaultDataTableCellTransformer
public Object transformer(Object fromValue, Type toValueType) {
return objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType));
}
}
package com.example
import com.fasterxml.jackson.databind.ObjectMapper
import io.cucumber.java.DefaultDataTableCellTransformer
import io.cucumber.java.DefaultDataTableEntryTransformer
import io.cucumber.java.DefaultParameterTransformer
import java.lang.reflect.Type
class StepDefinitions {
private val objectMapper = ObjectMapper()
@DefaultParameterTransformer
@DefaultDataTableEntryTransformer
@DefaultDataTableCellTransformer
fun transformer(fromValue: Any, toValueType: Type): Any {
return objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
}
package com.example
import com.fasterxml.jackson.databind.ObjectMapper
import io.cucumber.scala.ScalaDsl
import java.lang.reflect.Type
class StepDefinitions extends ScalaDsl {
private val objectMapper = ObjectMapper()
DefaultParameterTransformer { (fromValue: String, toValueType: Type) =>
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
DefaultDataTableCellTransformer { (fromValue: String, toValueType: Type) =>
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
DefaultDataTableEntryTransformer { (fromValue: Map[String, String], toValueType: Type) =>
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
}
package com.example;
import io.cucumber.java8.En;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Type;
public class LambdaStepDefinitions implements En {
public LambdaStepDefinitions() {
ObjectMapper objectMapper = new ObjectMapper();
DefaultParameterTransformer((String fromValue, Type toValueType) ->
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType)));
DefaultDataTableCellTransformer((fromValue, toValueType) ->
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType)));
DefaultDataTableEntryTransformer((fromValue, toValueType) ->
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType)));
}
}
import com.fasterxml.jackson.databind.ObjectMapper
import io.cucumber.java8.En
import java.lang.reflect.Type
class LambdaStepDefinitions : En {
init {
val objectMapper = ObjectMapper()
DefaultParameterTransformer { fromValue: String, toValueType: Type ->
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
DefaultDataTableCellTransformer { fromValue, toValueType ->
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
DefaultDataTableEntryTransformer { fromValue, toValueType ->
objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType))
}
}
}
For example, let’s define our own “Person” type:
ParameterType(
name: 'person',
regexp: /[A-Z][a-z]+/,
transformer: -> (name) { Person.new(name) }
)
Here’s an example of how to use this in a step definition:
Then /^a user {person} should have {int} followers$/ do |person, count|
assert(person.is_a?(Person))
end
The parameter type "person" is not defined.
The parameter type "person" is not defined.
The parameter type "person" is not defined.
The parameter type "person" is not defined.
For more information on how to use Parameter Type
with Cucumber-js, please see the parameter_types.feature.
For more information on how to use Data Tables
with Cucumber-js, please see the cucumber-js documentation.
Recommended location
The recommended location to define custom parameter types, would be in features/support/parameter_types.rb
. features/support/parameter_types.js
. src/test/java/com/example/ParameterTypes.java
. src/test/kotlin/com/example/ParameterTypes.kt
. src/test/kotlin/com/example/ParameterTypes.scala
.
This is just a convention though; Cucumber will pick them up from any file under features. under features. on the glue path. on the glue path. on the glue path.
Profiles
For instance, we can configure separate profiles for scenarios which are to be run in separate environments like so:
<profiles>
<profile>
<id>dev</id>
<properties>
<cucumber.filter.tags>@dev and not @ignore</cucumber.filter.tags>
</properties>
</profile>
<profile>
<id>qa</id>
<properties>
<cucumber.filter.tags>@qa</cucumber.filter.tags>
</properties>
</profile>
</profiles>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<configuration>
<systemPropertyVariables>
<cucumber.filter.tags>${cucumber.filter.tags}</cucumber.filter.tags>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
To mimick similar behavior using Gradle, see the Gradle docs on Migrating Maven profiles and properties.
For instance, we can configure separate profiles for scenarios which are to be run in separate environments like so:
<profiles>
<profile>
<id>dev</id>
<properties>
<cucumber.filter.tags>@dev and not @ignore</cucumber.filter.tags>
</properties>
</profile>
<profile>
<id>qa</id>
<properties>
<cucumber.filter.tags>@qa</cucumber.filter.tags>
</properties>
</profile>
</profiles>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<configuration>
<systemPropertyVariables>
<cucumber.filter.tags>${cucumber.filter.tags}</cucumber.filter.tags>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
To mimick similar behavior using Gradle, see the Gradle docs on Migrating Maven profiles and properties.
For instance, we can configure separate profiles for scenarios which are to be run in separate environments like so:
<profiles>
<profile>
<id>dev</id>
<properties>
<cucumber.filter.tags>@dev and not @ignore</cucumber.filter.tags>
</properties>
</profile>
<profile>
<id>qa</id>
<properties>
<cucumber.filter.tags>@qa</cucumber.filter.tags>
</properties>
</profile>
</profiles>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<configuration>
<systemPropertyVariables>
<cucumber.filter.tags>${cucumber.filter.tags}</cucumber.filter.tags>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
To mimick similar behavior using Gradle, see the Gradle docs on Migrating Maven profiles and properties.
config/cucumber.yml
## ##YAML Template
html_report: --format progress --format html --out=features_report.html
bvt: --tags @bvt
Defining a template requires a name and then the command-line options that you want to execute with this profile.
The example above generates two profiles:
html_report
, with a list of command-line options that specify new output formats, andbvt
, which executes all Features and Scenarios tagged with@bvt
.
Cucumber-Rails creates a cucumber.yml
file in the project config directory containing a number of predefined profiles,
one of which is the default profile. When Cucumber is run from the command line, it is usually necessary to provide both
the directory name containing the root directory of the tree containing feature files and the directory name containing
references to the necessary library files. In a typical project, cucumber --require features features/some/path
will suffice.
Repetitious usages can be added to user-defined profiles contained in the project’s cucumber.yml
file.
To execute the profile, use:
\[user@system project] cucumber --profile html_report
\[user@system project] cucumber -p bvt
Use the flag --profile
or -p
to execute Cucumber with a profile.
You can still use other command line arguments alongside --profile
or -p
,
if desired.
\[user@system project] cucumber --profile html_report --tags ~@wip
Multiple profiles can even be specified together. The following executes all
Features and Scenarios tagged @bvt
, with the specified progress and HTML
output.
\[user@system project] cucumber -p html_report -p bvt
Default Profile
Using the same example, perhaps we want the html_report
profile to be our default execution.
1. config/cucumber.yml
## ##YAML Template
default: --profile html_report --profile bvt
html_report: --format progress --format html --out=features_report.html
bvt: --tags @bvt
\[user@system project] cucumber
With this setup, Cucumber will now use both the bvt
profile and html_report
profile, testing all Features and Scenarios tagged with @bvt
, along with the
progress output and HTML output.
Preprocessing with ERB
The cucumber.yml
file is preprocessed by ERB (Embedded RuBy). This allows you to use Ruby code
to generate values in the cucumber.yml
file.
So, if you have several profiles with similar values, you might do this:
1. config/cucumber.yml
## ##YAML Template
<% common = "--tags ~@wip --strict" %>
default: <%= common %> features
html_report: <%= common %> --format html --out=features_report.html features
Environment Variables
1. config/cucumber.yml
\##YAML Template
2. ## ie profile executes the browser features with Internet Explorer
default: --profile html_report --profile bvt
html_report: --format progress --format html --out=features_report.html
bvt: --tags @bvt
ie: BROWSER=IE
When running Cucumber, it can sometimes be handy to pass special values to Cucumber for your step definitions to use.
You can do this on the command line:
cucumber FOO=BAR --format progress features
You can now pick up ENV\['FOO']
in Ruby (for example, in env.rb
, or a Step Definition) and perform actions according to the value.
You can also do this in cucumber.yml
.
For example, the following sets up a profile that runs the specified Tag and sets an environment variable:
baz: --tags @mytag FOO=BAR
Local Cucumber customisation code in support/env.rb
itself as that file is typically
overwritten by script/generate cucumber:install | rails g cucumber
. Customisations that
must be loaded before the rest of Cucumber initialises must be placed at the beginning of the env.rb file
.
Every file ending in .rb
that is found in features/support is loaded by Cucumber. Therefore, if you place local
customisations in any .rb
file in that directory, they will get loaded. However, be advised that in
Cucumber < 4.x, the --dry-run
option only excludes files in features/support
that match the regexp /env\\..\*/
(note that the trailing dot is significant). So a file with local customisations called my_locals.rb
will be loaded regardless.
In Cucumber 4.x, all support files, including env.rb
files, are loaded.
If you put custom files inside features/support
that you do not wish loaded when you do a dry-run with Cucumber,
you can use the --exclude
flag to ensure they aren’t loaded. In Cucumber versions < 4.x, you can also prefix the filenames
with env
, as in env.local.rb
. Note that a file called local_env.rb
, for example, does not match the regex
above and therefore will be loaded in all versions of Cucumber unless specifically excluded.
As a matter of good practice you should always run script/generate cucumber | rails g cucumber:install
whenever you
install an updated version of Cucumber or cucumber-rails. However, this overwrites features/support/env.rb
.
In order to keep any custom configurations from your env.rb
file, check in your env.rb
along with the rest of your version
controlled files and be prepared to diff and merge changes to env.rb
between versions of Cucumber-Rails.