Getting Started with Cucumber for Elixir

View Source

Cucumber for Elixir allows you to write executable specifications using natural language that bridges the gap between technical and non-technical stakeholders.

Overview

The Cucumber framework allows you to write tests in a natural language format using Gherkin syntax. This approach enables:

  • Behavior-driven development (BDD)
  • Clear documentation of application behavior
  • Tests that serve as living documentation
  • Auto-discovery of feature files and step definitions

Installation

Add cucumber to your mix.exs dependencies:

def deps do
  [
    {:cucumber, "~> 0.4.1"}
  ]
end

Quick Start

1. Configure Test Helper

Add Cucumber to your test/test_helper.exs:

ExUnit.start()
Cucumber.compile_features!()

2. Create a Feature File

Feature files use the Gherkin syntax and should be placed in test/features/ with a .feature extension.

# test/features/user_authentication.feature
Feature: User Authentication

Background:
  Given the application is running

Scenario: User signs in with valid credentials
  Given I am on the sign in page
  When I enter "user@example.com" as my email
  And I enter "password123" as my password
  And I click the "Sign In" button
  Then I should be redirected to the dashboard
  And I should see "Welcome back" message

3. Create Step Definitions

Create step definitions in test/features/step_definitions/ with a .exs extension:

# test/features/step_definitions/authentication_steps.exs
defmodule AuthenticationSteps do
  use Cucumber.StepDefinition
  import ExUnit.Assertions

  # Step definitions
  step "the application is running" do
    # Setup code here
    %{app_started: true}
  end

  step "I am on the sign in page", context do
    # Navigate to sign in page
    Map.put(context, :current_page, :sign_in)
  end

  step "I enter {string} as my email", %{args: [email]} = context do
    # Code to enter email
    Map.put(context, :email, email)
  end

  step "I enter {string} as my password", %{args: [password]} = context do
    # Code to enter password
    Map.put(context, :password, password)
  end

  step "I click the {string} button", %{args: [button_text]} do
    # Code to click button
    {:ok, %{clicked: button_text}}
  end

  step "I should be redirected to the dashboard", context do
    # Assertions for redirection
    assert context.current_page == :dashboard
    context
  end

  step "I should see {string} message", %{args: [message]} do
    # Assertion for message
    assert_text(message)
    :ok
  end
end

4. Run Your Tests

Run your tests using the standard mix test command:

# Run all tests including Cucumber
mix test

# Run only Cucumber tests
mix test --only cucumber

# Run tests for a specific feature
mix test --only feature_user_authentication

File Structure

Cucumber automatically discovers features and steps using this structure:

test/
  features/                      # Feature files
    user_authentication.feature
    shopping_cart.feature
    step_definitions/           # Step definition files
      authentication_steps.exs
      shopping_steps.exs
      common_steps.exs

Async Test Execution

For features that don't share state, you can enable concurrent test execution by adding the @async tag:

# test/features/calculator.feature
@async
Feature: Calculator Operations
  Independent calculations that can run concurrently

Scenario: Addition
  Given I have numbers 10 and 20
  When I add them together
  Then the result should be 30

Scenario: Multiplication
  Given I have numbers 5 and 6
  When I multiply them
  Then the result should be 30

Async features run concurrently with other async tests, improving test suite performance. Only use @async for features that are truly independent and don't rely on test execution order.

When using Ecto, async tests work well with the SQL sandbox in shared mode. See your application's test setup for database configuration.

Next Steps

For more detailed information about the Cucumber for Elixir framework, check out these guides: