Cucumber.Hooks (Cucumber v1.0.0)

View Source

Provides hooks for setup and teardown in Cucumber tests.

Four levels of hooks are supported, mirroring reference Cucumber implementations:

  • before_all/after_all — run once per test run (see below)
  • before_scenario/after_scenario — run around each scenario
  • before_step/after_step — run around each step (including background steps)

Scenario and step hooks can be filtered by tag. Hooks of the same kind run in the order they are defined (module order, then definition order), with after-hooks running in reverse. Any hook can be given a name:, which appears in failure output (and in Cucumber Messages, eventually).

Examples

defmodule DatabaseSupport do
  use Cucumber.Hooks

  # Runs once before the first scenario of the run
  before_all context do
    {:ok, Map.put(context, :api_started, true)}
  end

  # Runs once after the whole run, in reverse definition order
  after_all _context do
    :ok
  end

  # Global before hook - runs for all scenarios
  before_scenario context, name: "prepare database" do
    {:ok, Map.put(context, :setup, true)}
  end

  # Tagged before hook - only runs for @database scenarios
  before_scenario "@database", context do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)

    if context.async do
      Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
    end

    {:ok, context}
  end

  # After hooks run in reverse order
  after_scenario _context do
    # Cleanup code
    :ok
  end

  # Step hooks bracket every step; after_step sees the step's status
  after_step context do
    log_step(context.step, context.step_status)
    :ok
  end
end

Run-level hooks

before_all hooks run lazily, exactly once per run, before the first scenario that executes (serialized through Cucumber.RunCoordinator, so this is safe with @async features). Their accumulated context map is merged into every scenario's context. If a before_all hook fails, the remaining before_all hooks still run (to set up as much as possible for cleanup), but every scenario in the run fails with the original error.

after_all hooks run once after the whole suite via ExUnit.after_suite/1, in reverse definition order, receiving the before_all context plus the ExUnit suite summary under :suite_result. A failing after_all hook fails the run; the remaining after_all hooks still run.

Run-level hooks cannot be tagged (they don't belong to any scenario).

One hook per kind — unless named

A module may define only one global and one per-tag hook of each kind; giving hooks a name: lifts that restriction, so a module can define any number of distinctly-named hooks of the same kind.

Summary

Types

Wraps each hook invocation — receives the hook's run-order index, its name (nil if unnamed), and a one-arity function that executes the hook with the given context overrides merged in; must return that function's result. The scenario runner uses this to bracket hooks with Cucumber Messages events and to inject each hook's message reference.

Functions

Defines an after_all hook that runs once after the whole test run, in reverse definition order.

Defines an after_scenario hook that runs after each scenario.

Defines an after_step hook that runs after each step (including failing ones) of matching scenarios.

Defines a before_all hook that runs once per test run, before the first scenario that executes.

Defines a before_scenario hook that runs before each scenario.

Defines a before_step hook that runs before each step (including background steps) of matching scenarios.

Types

around()

@type around() :: (non_neg_integer(), String.t() | nil, (map() -> term()) -> term())

Wraps each hook invocation — receives the hook's run-order index, its name (nil if unnamed), and a one-arity function that executes the hook with the given context overrides merged in; must return that function's result. The scenario runner uses this to bracket hooks with Cucumber Messages events and to inject each hook's message reference.

hook()

@type hook() :: {hook_type(), String.t() | nil, String.t() | nil, {module(), atom()}}

hook_type()

@type hook_type() ::
  :before_scenario
  | :after_scenario
  | :before_step
  | :after_step
  | :before_all
  | :after_all

Functions

after_all(context_var, list)

(macro)

Defines an after_all hook that runs once after the whole test run, in reverse definition order.

The hook receives the before_all context with the ExUnit suite summary merged under :suite_result. A raise or {:error, reason} fails the run, but the remaining after_all hooks still run. Cannot be tagged; can be given a name:.

after_all(context_var, opts, list)

(macro)

after_scenario(context_var, list)

(macro)

Defines an after_scenario hook that runs after each scenario.

Can optionally be filtered by tag and/or given a name:. After hooks run in reverse order of definition and receive the post-background context. Their return values are ignored.

after_scenario(tag, context_var, list)

(macro)

after_scenario(tag, context_var, opts, list)

(macro)

after_step(context_var, list)

(macro)

Defines an after_step hook that runs after each step (including failing ones) of matching scenarios.

Can optionally be filtered by tag and/or given a name:. The hook receives the step's context with :step_status set to :passed, :failed, :pending, or :skipped. After-step hooks run in reverse order of definition; their return values are ignored.

after_step(tag, context_var, list)

(macro)

after_step(tag, context_var, opts, list)

(macro)

before_all(context_var, list)

(macro)

Defines a before_all hook that runs once per test run, before the first scenario that executes.

The hook receives the accumulated before_all context (starting from an empty map) and may return :ok, a map, a keyword list, {:ok, map}, or {:error, reason}. The final context map is merged into every scenario's context. Cannot be tagged; can be given a name:.

before_all(context_var, opts, list)

(macro)

before_scenario(context_var, list)

(macro)

Defines a before_scenario hook that runs before each scenario.

Can optionally be filtered by tag and/or given a name:. The hook receives the test context and must return one of:

  • {:ok, context}
  • :ok (keeps context unchanged)
  • map (merged into context)
  • keyword list (merged into context)
  • {:error, reason} (fails the scenario; steps and after hooks don't run)
  • :skipped / {:skipped, reason} (skips the whole scenario without failing it; remaining before hooks and all steps are skipped, after hooks still run)
  • :pending / {:pending, message} (like :skipped, but the scenario fails with Cucumber.PendingStepError)

before_scenario(tag, context_var, list)

(macro)

before_scenario(tag, context_var, opts, list)

(macro)

before_step(context_var, list)

(macro)

Defines a before_step hook that runs before each step (including background steps) of matching scenarios.

Can optionally be filtered by tag and/or given a name:. The hook receives the step's prepared context — including :step (the Gherkin.Step), :args, and any :datatable/:docstring — and supports the same return values as before_scenario (a :skipped/:pending signal skips the rest of the scenario; {:error, reason} fails it without running the step body).

before_step(tag, context_var, list)

(macro)

before_step(tag, context_var, opts, list)

(macro)