Hooks
View SourceCucumber for Elixir provides hooks that allow you to run code before and after scenarios. This is useful for setup and teardown operations like database transactions, authentication, or any other cross-cutting concerns.
Overview
Hooks are defined in support files placed in test/features/support/ and are automatically discovered and executed at the appropriate times during test execution.
Defining Hooks
To define hooks, create a module that uses Cucumber.Hooks:
# test/features/support/database_support.exs
defmodule DatabaseSupport do
use Cucumber.Hooks
# Global hook - runs before every scenario
before_scenario context do
# Your setup code here
{:ok, Map.put(context, :setup_done, true)}
end
# Tagged hook - only runs for scenarios with @database tag
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 of definition
after_scenario _context do
# Cleanup code
:ok
end
endHook Types
Before Scenario Hooks
Run before each scenario:
# Global before hook
before_scenario context do
# Runs before every scenario
{:ok, context}
end
# Tagged before hook
before_scenario "@slow", context do
# Only runs for scenarios tagged with @slow
{:ok, Map.put(context, :timeout, 30_000)}
endAfter Scenario Hooks
Run after each scenario:
# Global after hook
after_scenario context do
# Runs after every scenario
:ok
end
# Tagged after hook
after_scenario "@api", context do
# Only runs for scenarios tagged with @api
# Clean up API state
:ok
endReturn Values
Hooks support the same return values as step definitions:
:ok- Keeps the context unchanged{:ok, map}- Merges the map into the context%{} = map- Merges the map into the context{:error, reason}- Fails the scenario before it starts
Hook Execution Order
- Before hooks run in the order they are defined
- After hooks run in reverse order (last defined runs first)
- Tagged hooks only run for scenarios with matching tags
- Global hooks run for all scenarios
Tag Inheritance
Feature-level tags are inherited by all scenarios in that feature:
@database
Feature: User Management
# All scenarios inherit @database tag
Scenario: Create user
# This scenario has @database tag
Given a new user
@api
Scenario: API user creation
# This scenario has both @database and @api tags
Given an API requestContext Variables
The context passed to hooks includes:
:scenario_name- The name of the current scenario:async- Whether the feature is running in async mode:step_history- List of steps executed (empty in before hooks)- Any data added by previous hooks or steps
Practical Examples
Database Setup
defmodule DatabaseSupport do
use Cucumber.Hooks
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
endAuthentication
defmodule AuthSupport do
use Cucumber.Hooks
before_scenario "@authenticated", context do
user = MyApp.Factory.insert(:user)
token = MyApp.Auth.generate_token(user)
{:ok, Map.merge(context, %{
current_user: user,
auth_token: token
})}
end
endPerformance Monitoring
defmodule PerformanceSupport do
use Cucumber.Hooks
before_scenario "@performance", context do
start_time = System.monotonic_time()
{:ok, Map.put(context, :start_time, start_time)}
end
after_scenario "@performance", context do
duration = System.monotonic_time() - context.start_time
milliseconds = System.convert_time_unit(duration, :native, :millisecond)
IO.puts("Scenario completed in #{milliseconds}ms")
:ok
end
endConfiguration
By default, support files are loaded from test/features/support/**/*.exs. You can customize this in your config:
# config/test.exs
config :cucumber,
support: ["test/support/**/*.exs", "test/cucumber_support/**/*.exs"]Best Practices
- Keep hooks focused - Each hook should have a single responsibility
- Use tags wisely - Don't create too many specialized hooks
- Avoid side effects - Hooks should be predictable and repeatable
- Clean up in after hooks - Ensure proper cleanup even if scenarios fail
- Use context passing - Share data between hooks and steps via context
Troubleshooting
Hooks not running
- Ensure your support files are in the correct directory
- Verify the module uses
Cucumber.Hooks - Check that tags match exactly (including the @ symbol)
- Confirm the file has a
.exsextension
Hook execution order
Remember that:
- Before hooks run in definition order
- After hooks run in reverse definition order
- Tagged hooks only run when tags match