Package {cucumber}


Type: Package
Title: Behavior-Driven Development for R
Version: 2.2.0
Description: Write executable specifications in a natural language that describes how your code should behave. Write specifications in feature files using 'Gherkin' language and execute them using functions implemented in R. Use them as an extension to your 'testthat' tests to provide a high level description of how your code works.
License: MIT + file LICENSE
URL: https://github.com/jakubsob/cucumber
BugReports: https://github.com/jakubsob/cucumber/issues
Encoding: UTF-8
Depends: R (≥ 4.1.0)
Imports: checkmate, cli, dplyr, fs, glue, purrr, R6, rlang, stringr, testthat (≥ 3.0.0), tibble, withr
Suggests: mockery, box, shinytest2, chromote, covr, knitr, rmarkdown, quarto, pkgdown, pkgload, muttest
Config/testthat/edition: 3
RoxygenNote: 7.3.3
VignetteBuilder: quarto
Config/Needs/website: rmarkdown
NeedsCompilation: no
Packaged: 2026-10-05 08:12:07 UTC; runner
Author: Jakub Sobolewski [aut, cre]
Maintainer: Jakub Sobolewski <jakupsob@gmail.com>
Repository: CRAN
Date/Publication: 2026-10-05 09:00:02 UTC

Progress Reporter for Cucumber

Description

A reporter that prints step-by-step progress to the console, mimicking testthat::ProgressReporter but with visibility into individual Gherkin steps.

Details

Prints each step as it executes with a status indicator, grouped under scenario and feature headers similar to testthat's output:

Feature: Addition
  Scenario: Add two numbers
    [x] Given I have entered 50 into the calculator
    [x] Given I have entered 70 into the calculator
    [x] When I press add
    [x] Then the result should be 120 on the screen

Usage

Pass a reporter instance to test() or run():

# Step-level detail
cucumber::test("tests/acceptance", reporter = cucumber::CucumberProgressReporter$new())

# testthat reporters still work, reporting at the scenario level only
cucumber::test("tests/acceptance", reporter = testthat::ProgressReporter$new())

# Silent mode
cucumber::test("tests/acceptance", reporter = testthat::SilentReporter$new())

# From within a test file
cucumber::run(reporter = cucumber::CucumberProgressReporter$new())

Creating custom reporters

Extend CucumberReporter to build your own:

HTMLReporter <- R6::R6Class(
  "HTMLReporter",
  inherit = cucumber::CucumberReporter,
  public = list(
    html_output = character(),
    start_feature = function(feature_name) {
      super$start_feature(feature_name)
      self$html_output <- c(self$html_output, sprintf("<h2>%s</h2>", feature_name))
    },
    end_step = function(step) {
      super$end_step(step)
      status_class <- step$status %||% "passed"
      self$html_output <- c(
        self$html_output,
        sprintf('<div class="step %s">%s %s</div>', status_class, step$keyword, step$text)
      )
    },
    end_reporter = function() {
      if (!is.null(super$end_reporter)) super$end_reporter()
      writeLines(self$html_output, "test-report.html")
    }
  )
)

Step metadata

When end_step() is called, the step object contains:

Super classes

testthat::Reporter -> cucumber::CucumberReporter -> CucumberProgressReporter

Public fields

show_praise

Whether to show praise messages

step_count

Number of steps executed

step_passed

Number of steps that passed

step_failed

Number of steps that failed

current_scenario

Name of the scenario currently executing

current_steps

Steps executed so far in the current scenario

failures

Recorded failures for the end-of-run summary

step_errored

Whether end_step already rendered an error this scenario

num_colors

Terminal color support captured at construction

reporter_max_docstring_lines

Max docstring lines to print per step

reporter_max_table_lines

Max data table rows to print per step

Methods

Public methods

Inherited methods

Method new()

Initialize the reporter

Usage
CucumberProgressReporter$new(
  show_praise = TRUE,
  reporter_max_docstring_lines = getOption("cucumber.reporter_max_docstring_lines", Inf),
  reporter_max_table_lines = getOption("cucumber.reporter_max_table_lines", Inf),
  ...
)
Arguments
show_praise

Whether to show praise (default TRUE)

reporter_max_docstring_lines

Max docstring lines to print for a step before truncating. Defaults to the cucumber.reporter_max_docstring_lines option, or Inf (print in full) if unset.

reporter_max_table_lines

Max data table rows to print for a step before truncating. Defaults to the cucumber.reporter_max_table_lines option, or Inf (print in full) if unset.

...

Additional arguments passed to parent


Method start_feature()

Start a feature

Usage
CucumberProgressReporter$start_feature(feature_name)
Arguments
feature_name

Name of the feature


Method end_feature()

End the current feature

Usage
CucumberProgressReporter$end_feature()

Method start_test()

Start a test (called by testthat for each scenario)

Usage
CucumberProgressReporter$start_test(context, test)
Arguments
context

Test context

test

Test name (will be "Scenario: ⁠<name>⁠")


Method add_result()

Add a result (warning, skip, failure, etc.)

Usage
CucumberProgressReporter$add_result(context, test, result)
Arguments
context

Test context

test

Test object

result

Test result object


Method start_step()

Start a step

Usage
CucumberProgressReporter$start_step(step)
Arguments
step

Pickle step object


Method end_step()

End a step and print its result

Usage
CucumberProgressReporter$end_step(step)
Arguments
step

Pickle step object with execution results


Method print_step_args()

Print a step's docstring and/or data table arguments, indented and truncated to reporter_max_docstring_lines / reporter_max_table_lines

Usage
CucumberProgressReporter$print_step_args(step)
Arguments
step

Pickle step object


Method end_reporter()

Print summary at the end (called by testthat)

Usage
CucumberProgressReporter$end_reporter()

Method clone()

The objects of this class are cloneable with this method.

Usage
CucumberProgressReporter$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.

See Also

CucumberReporter

Examples

## Not run: 
# Use with cucumber::test()
cucumber::test(
  "tests/acceptance",
  reporter = cucumber::CucumberProgressReporter$new()
)

# Or without praise messages
cucumber::test(
  "tests/acceptance",
  reporter = cucumber::CucumberProgressReporter$new(show_praise = FALSE)
)

## End(Not run)


Cucumber Reporter Base Class

Description

Base class for cucumber reporters that extends testthat::Reporter. Adds step-level and feature-level reporting hooks while maintaining full compatibility with testthat reporters.

Details

CucumberReporter extends testthat's Reporter class to add: - Feature-level hooks: 'start_feature()', 'end_feature()' - Step-level hooks: 'start_step()', 'end_step()'

These additional hooks are called during cucumber test execution to provide visibility into individual step execution. Standard testthat reporters will continue to work unchanged, reporting only at the scenario (test) level.

Methods

'start_feature(feature_name)'

Called before executing a feature's scenarios

'end_feature()'

Called after all scenarios in a feature complete

'start_step(step)'

Called before executing a step

'end_step(step)'

Called after a step completes, with status/error/duration populated

Super class

testthat::Reporter -> CucumberReporter

Public fields

current_feature

Current feature name being executed

current_pickle

Current pickle (scenario) being executed

Methods

Public methods

Inherited methods

Method start_feature()

Start a feature

Usage
CucumberReporter$start_feature(feature_name)
Arguments
feature_name

Name of the feature


Method end_feature()

End the current feature

Usage
CucumberReporter$end_feature()

Method start_step()

Start a step

Usage
CucumberReporter$start_step(step)
Arguments
step

Pickle step object


Method end_step()

End a step (called after execution with status/error/duration populated)

Usage
CucumberReporter$end_step(step)
Arguments
step

Pickle step object with execution results


Method clone()

The objects of this class are cloneable with this method.

Usage
CucumberReporter$clone(deep = FALSE)
Arguments
deep

Whether to make a deep clone.


Define extra parameters to use in Cucumber steps.

Description

The following parameter types are available by default:

Type Description
{int} Matches integers, for example 71 or -19. Converts value with as.integer.
{float} Matches floats, for example 3.6, .8 or -9.2. Converts value with as.double.
{word} Matches words without whitespace, for example banana (but not ⁠banana split⁠).
{string} Matches single-quoted or double-quoted strings, for example "banana split" or 'banana split' (but not ⁠banana split⁠). Only the text between the quotes will be extracted. The quotes themselves are discarded.

To use custom parameter types, call define_parameter_type before cucumber::test is called.

Usage

define_parameter_type(name, regexp, transformer)

Arguments

name

The name of the parameter.

regexp

A regular expression that the parameter will match on. Note that if you want to escape a special character, you need to use four backslashes.

transformer

A function that will transform the parameter from a string to the desired type. Must be a function that requires only a single argument.

Value

An object of class parameter, invisibly. Function should be called for side effects.

Examples

define_parameter_type("color", "red|blue|green", as.character)
define_parameter_type(
  name = "sci_number",
  regexp = "[+-]?\\\\d*\\\\.?\\\\d+(e[+-]?\\\\d+)?",
  transform = as.double
)

## Not run: 
#' tests/testthat/test-cucumber.R
cucumber::define_parameter_type("color", "red|blue|green", as.character)
cucumber::test(".", "./steps")

## End(Not run)


Hooks

Description

Hooks are blocks of code that can run at various points in the Cucumber execution cycle. They are typically used for setup and teardown of the environment before and after each scenario.

Where a hook is defined has no impact on what scenarios it is run for.

If you want to run a hook only before or after a specific scenario, use it's name to execute hook only for this scenario.

Usage

before(hook)

after(hook)

Arguments

hook

A function that will be run. The function first argument is context and the scenario name is the second argument.

Before

Whatever happens in a before hook is invisible to people who only read the features. You should consider using a background as a more explicit alternative, especially if the setup should be readable by non-technical people. Only use a before hook for low-level logic such as starting a browser or deleting data from a database.

After

After hooks run after the last step of each scenario, even when the scenario failed or thrown an error.

Examples

## Not run: 
before(function(context, scenario_name) {
  context$session <- selenider::selenider_session()
})

after(function(context, scenario_name) {
  selenider::close_session(context$session)
})

after(function(context, scenario_name) {
  if (scenario_name == "Playing one round of the game") {
    context$game$close()
  }
})

## End(Not run)

cucumber Options

Description

Internally used, package-specific options. They allow overriding the default behavior of the package.

Details

The following options are available:

See base::options() and base::getOption() on how to work with options.


Mark a step as pending

Description

Call 'pending()' inside a step implementation to signal that the step is not yet implemented. The scenario will be reported as skipped rather than failed.

Usage

pending(message = "TODO")

Arguments

message

A message explaining why the step is pending.

Details

This is useful when you want to write your feature files first and implement the steps later, or when you want to temporarily disable a step without deleting its implementation.

Examples

given("I have {int} cucumbers in my basket", function(n, context) {
  pending("not yet implemented")
})


Run Cucumber tests in a testthat context

Description

It's purpose is to be able to run Cucumber tests alongside testthat tests.

To do that, place a call to run() in one of the ⁠test-*.R⁠ files in your tests/testthat directory.

Usage

run(path = ".", filter = NULL, tags = NULL, reporter = get_reporter(), ...)

Arguments

path

Path to the directory containing the .feature files. If run() is placed in a ⁠tests/testthat/test-*.R⁠ file and you call testthat::test_dir or similar, it runs in the tests/testthat directory. The default value "." finds all feature files in the tests/testthat directory.

filter

If not NULL, only features with file names matching this regular expression will be executed. Matching is performed on the file name after it's stripped of ".feature".

tags

If not NULL, filter scenarios by tag expression string (e.g., "@smoke and not @slow", "@gui or @database"). Tag expressions support and, or, not operators and parentheses for grouping.

reporter

Optional reporter instance (testthat::Reporter or cucumber::CucumberReporter). If NULL, will use reporter from package options if available.

...

Additional arguments passed to grepl().

Value

NULL, invisibly. To get result and a report, use cucumber::test(), or inspect the result of testthat function call.

Examples

## Not run: 
#' tests/testthat/test-cucumber.R
cucumber::run()

## End(Not run)


Define a step

Description

Provide a description that matches steps in feature files and the implementation function that will be run.

Usage

given(description, implementation)

when(description, implementation)

then(description, implementation)

Arguments

description

A description of the step.

Cucumber executes each step in a scenario one at a time, in the sequence you’ve written them in. When Cucumber tries to execute a step, it looks for a matching step definition to execute.

Keywords are not taken into account when looking for a step definition. This means you cannot have a Given, When, Then, And or But step with the same text as another step.

Cucumber considers the following steps duplicates:

Given there is money in my account
Then there is money in my account

This might seem like a limitation, but it forces you to come up with a less ambiguous, more clear domain language:

Given my account has a balance of £430
Then my account should have a balance of £430

To pass arguments, description can contain placeholders in curly braces.

To match:

Given my account has a balance of £430

use:

given("my account has a balance of £{float}", function(balance, context) {

})

If no step definition is found an error will be thrown.

If multiple steps definitions for a single step are found an error will be thrown.

implementation

A function that will be run during test execution.

The implementation function must always have the last parameter named context. It holds the environment where test state can be stored to be passed to the next step.

If a step has a description "I have {int} cucumbers in my basket" then the implementation function should be ⁠function(n, context)⁠. The {int} value will be passed to n, this parameter can have any name.

If a table or a docstring is defined for a step, it will be passed as an argument after placeholder parameters and before context. The function should be a ⁠function(n, table, context)⁠. See an example on how to write implementation that uses tables or docstrings.

Details

Placeholders in expressions are replaced with regular expressions that match values in the feature file. Regular expressions are generated during runtime based on defined parameter types.

The expression "I have {int} cucumbers in my basket" will be converted to "I have [+-]?(?<![.])[:digit:]+(?![.]) cucumbers in my basket". The extracted value of {int} will be passed to the implementation function after being transformed with as.integer.

To define your own parameter types use define_parameter_type.

Value

A function of class step, invisibly. Function should be called for side effects.

See Also

define_parameter_type()

Examples

given("I have {int} cucumbers in my basket", function(n_cucumbers, context) {
  context$n_cucumbers <- n_cucumbers
})

given("I have {int} cucumbers in my basket and a table", function(n_cucumbers, table, context) {
  context$n_cucumbers <- n_cucumbers
  context$table <- table
})

when("I eat {int} cucumbers", function(n_cucumbers, context) {
  context$n_cucumbers <- context$n_cucumbers - n_cucumbers
})

then("I should have {int} cucumbers in my basket", function(n_cucumbers, context) {
  expect_equal(context$n_cucumbers, n_cucumbers)
})


Run Cucumber tests

Description

It runs tests from specifications in .feature files found in the path.

To run Cucumber tests alongside testthat tests, see cucumber::run().

Usage

test(
  path = "tests/acceptance",
  filter = NULL,
  tags = NULL,
  reporter = get_reporter(),
  env = NULL,
  load_helpers = TRUE,
  stop_on_failure = TRUE,
  stop_on_warning = FALSE,
  ...
)

Arguments

path

Path to directory containing tests.

filter

If not NULL, only features with file names matching this regular expression will be executed. Matching is performed on the file name after it's stripped of ".feature".

tags

If not NULL, filter scenarios by tag expression string (e.g., "@smoke and not @slow", "@gui or @database"). Tag expressions support and, or, not operators and parentheses for grouping.

reporter

Reporter to use to summarise output. Can be supplied as a string (e.g. "summary") or as an R6 object (e.g. SummaryReporter$new()).

See Reporter for more details and a list of built-in reporters.

env

Environment in which to execute the tests. Expert use only.

load_helpers

Source helper files before running the tests?

stop_on_failure

If TRUE, throw an error if any tests fail.

stop_on_warning

If TRUE, throw an error if any tests generate warnings.

...

Additional arguments passed to grepl() to control filtering.

Good Practices

Examples

## Not run: 
cucumber::test("tests/acceptance")
cucumber::test("tests/acceptance", filter = "addition|multiplication")

# Tag expressions
cucumber::test("tests/acceptance", tags = "@smoke")
cucumber::test("tests/acceptance", tags = "@smoke and @fast")
cucumber::test("tests/acceptance", tags = "@wip and not @slow")
cucumber::test("tests/acceptance", tags = "(@smoke or @ui) and (not @slow)")

## End(Not run)


Validate lines read from a feature file

Description

Validate lines read from a feature file

Usage

validate_feature(lines)