docs/error_handling.md

# Error Handling and Debugging

Cucumber for Elixir provides detailed error reporting to help debug test failures. This document explains how to handle and understand errors in your Cucumber tests.

## Types of Errors

### Missing Step Definition

When a step in your feature file has no matching definition, the framework will provide a helpful error message:

```
** (Cucumber.StepError) No matching step definition found for step:

  When I try to use a step with no definition

in scenario "Missing Step Example" (test/features/example.feature:6)

Please define this step with:

step "I try to use a step with no definition", context do
  # Your step implementation here
  context
end
```

This error not only tells you which step is missing, but also provides a template for implementing the missing step definition.

### Failed Step

When a step fails during execution (due to a failed assertion or an exception), you'll get an error like this:

```
** (Cucumber.StepError) Step failed:

  Then the validation should succeed

in scenario "Form Submission" (test/features/forms.feature:12)
matching pattern: "the validation should succeed"

Validation failed: invalid input data

Step execution history:
  [passed] Given a form to fill out
  [passed] When I submit invalid data
  [failed] Then the validation should succeed
```

The error message includes:
- Which step failed
- The scenario and file where the failure occurred
- The step pattern that matched
- The error message from the step implementation
- The execution history, showing which steps passed and which failed

### Syntax Errors in Feature Files

If there are syntax errors in your feature files, you'll get an error when the parser tries to process the file:

```
** (Cucumber.ParseError) Syntax error in feature file:

  test/features/invalid.feature:5

Expected a scenario or background but found:
  
  Invalid line that doesn't start with a Gherkin keyword
```

## Step Failure Handling

Step definitions can indicate failure in several ways:

### 1. Assertions

Using ExUnit assertions will cause the step to fail if the assertion fails:

```elixir
step "the total should be {float}", %{args: [total]} = context do
  assert context.cart_total == total
  :ok
end
```

### 2. Raising Exceptions

Any uncaught exception will cause the step to fail:

```elixir
step "I click the submit button", _context do
  raise "The submit button is disabled"
  :ok
end
```

### 3. Returning `{:error, reason}`

You can explicitly return an error tuple to fail a step:

```elixir
step "the payment should be successful", context do
  if context.payment_status == :success do
    :ok
  else
    {:error, "Expected payment to succeed, but got status: #{context.payment_status}"}
  end
end
```

## Debugging Tips

### 1. Use IO.inspect for Debug Output

Insert `IO.inspect` calls to see the values of variables during test execution:

```elixir
step "I should see my order summary", context do
  IO.inspect(context, label: "Context in order summary step")
  assert context.order != nil
  :ok
end
```

### 2. Add Step Execution Logs

Log information about step execution:

```elixir
step "I complete the checkout process", context do
  IO.puts("Starting checkout process")
  # Checkout logic
  IO.puts("Completed checkout process")
  :ok
end
```

### 3. Examine the Full Context

Print the full context at any point to see the accumulated state:

```elixir
step "I check my context", context do
  IO.inspect(context, label: "Current context", pretty: true)
  :ok
end
```

### 4. Create Debug-Only Steps

Add steps specifically for debugging:

```elixir
step "I debug my test state", context do
  IO.puts("==== DEBUG STATE ====")
  IO.inspect(context.current_page, label: "Current Page")
  IO.inspect(context.user, label: "Current User")
  IO.inspect(context.cart_items, label: "Cart Items")
  IO.puts("==== END DEBUG ====")
  :ok
end
```

## Handling Flaky Tests

Sometimes tests can be inconsistent due to timing issues, especially with UI interactions:

```elixir
step "I should see the confirmation message", context do
  # Add delay or retry logic for UI-related assertions
  :timer.sleep(500)  # Give the UI time to update
  assert_text("Your order has been confirmed")
  :ok
end
```

Consider implementing retry logic for flaky steps:

```elixir
defp retry_until(function, max_attempts \\ 5, delay \\ 100) do
  Enum.reduce_while(1..max_attempts, nil, fn attempt, _acc ->
    case function.() do
      {:ok, result} -> {:halt, {:ok, result}}
      {:error, reason} ->
        if attempt == max_attempts do
          {:halt, {:error, reason}}
        else
          :timer.sleep(delay)
          {:cont, nil}
        end
    end
  end)
end

step "I should see the success notification", _context do
  result = retry_until(fn ->
    if element_visible?(".success-notification") do
      {:ok, true}
    else
      {:error, "Success notification not visible"}
    end
  end)
  
  case result do
    {:ok, _} -> :ok
    {:error, reason} -> {:error, reason}
  end
end
```

## Common Error Patterns and Solutions

| Error Pattern | Possible Cause | Solution |
|---------------|----------------|----------|
| Step not found | Step definition missing or typo in step | Create the missing step definition or correct the typo |
| Assertion failure | Expected value doesn't match actual | Check your test data and application logic |
| Timeout | Asynchronous operation didn't complete in time | Increase timeout or add retry logic |
| Element not found | UI element not rendered yet or selector wrong | Add delay, retry, or correct the selector |
| Context key missing | Previous step didn't set expected data | Ensure required context keys are set in earlier steps |