Writing E2E Scenarios
This guide walks through adding a new end-to-end test scenario using Gherkin syntax and godog step definitions.
Where Feature Files Live
All feature files are in:
crawbl-backend/internal/testsuite/e2e/features/
Files are numbered by domain area (e.g., 01_health.feature, 06_chat.feature). Add new scenarios to the existing file that matches your domain, or create a new numbered file for a genuinely new domain.
Gherkin Syntax Basics
Gherkin uses a structured English format:
Feature: Integrations
Scenarios for creating and managing integrations.
Scenario: Create an integration
Given the user "primary" is authenticated
When I send a POST request to "/v1/integrations" with body:
"""
{
"workspace_id": "{{primary_workspace_id}}",
"provider": "slack"
}
"""
Then the response status should be 201
And the response body should contain "slack"
Key rules:
Featuredescribes the area being tested.Scenariodescribes one specific test case.Givensets up preconditions.Whenperforms the action under test.Thenasserts the expected outcome.Andchains multiple steps of the same type.
Step Definition Files
Step definitions map Gherkin steps to Go code. They live alongside the feature files:
| File | Purpose |
|---|---|
steps_http.go | HTTP request steps (When I send a POST/GET/DELETE request to ...) |
steps_assert.go | Response assertion steps (Then the response status should be ...) |
steps_db.go | Database assertion steps (verify rows exist, check column values) |
steps_user.go | User lifecycle steps (authentication, user creation) |
steps_state.go | Value interpolation ({{primary_workspace_id}}, {{last_response_id}}) |
Adding a New Scenario
Choose the feature file
Add the scenario to the existing domain file when possible. For a new integrations area, that might be:
14_integrations.feature
Write the Gherkin
Start with the scenario in plain Gherkin.
Scenario: Create a Slack integration
Given the user "primary" is authenticated
And the user "primary" has a workspace
When I send a POST request to "/v1/workspaces/{{primary_workspace_id}}/integrations" with body:
"""
{
"provider": "slack"
}
"""
Then the response status should be 201
And the response JSON at "provider" should equal "slack"
Reuse existing step definitions
Most scenarios already fit the existing Given, When, and Then helpers.
Given the user "primary" is authenticated
When I send a GET request to "/v1/users/profile"
Then the response status should be 200
And I store the response JSON at "id" as "integration_id"
Add new steps only if needed
If a step does not exist, add it to the matching steps_*.go file and register it in the godog suite.
ctx.Step(`^the response JSON array should be empty$`, s.theResponseJSONArrayShouldBeEmpty)
Run the scenario
Start with the narrowest loop first, then widen if it passes.
./crawbl test e2e --base-url http://localhost:7171 -v
Tips
- Use the 3 test users (
primary,frank,grace) — do not create new users per scenario. - Use value interpolation (
{{...}}) to reference IDs from previous steps. - Keep scenarios independent — each scenario should work regardless of execution order within the same user context.
- Test failure cases — include 400, 401, 403, and 404 scenarios alongside happy paths.
What's next: Test Architecture