- Create commit_message skill with Gitmoji validation and templates - Update bdd_testing skill to match validated BDD implementation - Add comprehensive documentation and validation scripts - Ensure all skills follow AGENTS.md conventions Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
13 KiB
name, description, license, metadata
| name | description | license | metadata | ||||||
|---|---|---|---|---|---|---|---|---|---|
| bdd-testing | Behavior-Driven Development testing for DanceLessonsCoach using Godog. Use when creating or running BDD tests, implementing new features with BDD, or validating API endpoints through Gherkin scenarios. | MIT |
|
BDD Testing for DanceLessonsCoach
Behavior-Driven Development testing framework using Godog for the DanceLessonsCoach project. This skill provides comprehensive guidance for creating, running, and maintaining BDD tests that validate API endpoints and system behavior.
Key Concepts
Black Box Testing Principles
- External API Only: Tests interact only through public HTTP endpoints
- No Internal Access: No direct access to database, services, or internal components
- Real HTTP Requests: Actual network calls to verify system behavior
- Isolation: Each scenario runs with fresh client instances
Hybrid In-Process Testing
- Real Server Code: Uses actual server implementation running in-test process
- Fixed Port: Test server runs on port 9191
- No External Processes: Avoids complex process management
- Graceful Shutdown: Proper server lifecycle management
Commands
Run BDD Tests
go test ./features/...
Runs all BDD tests in the features directory using Godog test runner.
Arguments:
- None (uses standard Go test infrastructure)
Validate BDD Tests
./scripts/run-bdd-tests.sh
Validates BDD tests and fails if any undefined, pending, or skipped steps are found.
Arguments:
- None
Create New Feature
# Create new feature file
touch features/<feature_name>.feature
# Add Gherkin scenarios
# Implement step definitions in pkg/bdd/steps/
Arguments:
feature_name- Name of the feature (e.g., "greet", "health")
Workflows
Implementing a New BDD Feature
- Create Feature File: Define scenarios in Gherkin syntax
- Implement Steps: Add step definitions following Godog's exact patterns
- Run Tests: Execute and debug scenarios
- Validate: Ensure no undefined/pending steps
- Document: Add feature documentation
Debugging BDD Tests
- Check Step Patterns: Ensure steps match Godog's exact regex patterns
- Verify Server: Confirm test server is running on port 9191
- Inspect Responses: Check actual vs expected API responses
- Review Logs: Examine test output for undefined steps
- Validate JSON: Ensure proper JSON escaping in feature files
Usage Examples
Creating a Greet Feature
# features/greet.feature
Feature: Greet Service
The greet service should return appropriate greetings
Scenario: Default greeting
Given the server is running
When I request the default greeting
Then the response should be "{\"message\":\"Hello world!\"}"
Scenario: Personalized greeting
Given the server is running
When I request a greeting for "John"
Then the response should be "{\"message\":\"Hello John!\"}"
Creating a Health Feature
# features/health.feature
Feature: Health Endpoint
The health endpoint should indicate server status
Scenario: Health check returns healthy status
Given the server is running
When I request the health endpoint
Then the response should be "{\"status\":\"healthy\"}"
Implementing Step Definitions
// pkg/bdd/steps/steps.go
func (sc *StepContext) theServerIsRunning() error {
// Actually verify the server is running by checking the readiness endpoint
return sc.client.Request("GET", "/api/ready", nil)
}
func (sc *StepContext) iRequestAGreetingFor(name string) error {
return sc.client.Request("GET", fmt.Sprintf("/api/v1/greet/%s", name), nil)
}
func (sc *StepContext) iRequestTheDefaultGreeting() error {
return sc.client.Request("GET", "/api/v1/greet/", nil)
}
func (sc *StepContext) iRequestTheHealthEndpoint() error {
return sc.client.Request("GET", "/api/health", nil)
}
func (sc *StepContext) theResponseShouldBe(arg1, arg2 string) error {
// The regex captures the full JSON from the feature file, including quotes
// We need to extract just the key and value without the surrounding quotes and backslashes
// Remove the surrounding quotes and backslashes
cleanArg1 := strings.Trim(arg1, `"\`)
cleanArg2 := strings.Trim(arg2, `"\`)
// Build the expected JSON string
expected := fmt.Sprintf(`{"%s":"%s"}`, cleanArg1, cleanArg2)
return sc.client.ExpectResponseBody(expected)
}
Registering Steps
// pkg/bdd/steps/steps.go
func InitializeAllSteps(ctx *godog.ScenarioContext, client *testserver.Client) {
sc := NewStepContext(client)
// Use Godog's EXACT regex patterns and parameter names
ctx.Step(`^I request a greeting for "([^"]*)"$`, sc.iRequestAGreetingFor)
ctx.Step(`^I request the default greeting$`, sc.iRequestTheDefaultGreeting)
ctx.Step(`^I request the health endpoint$`, sc.iRequestTheHealthEndpoint)
ctx.Step(`^the response should be "{\"([^"]*)\":\"([^"]*)"}"$`, sc.theResponseShouldBe)
ctx.Step(`^the server is running$`, sc.theServerIsRunning)
}
Gotchas
Step Pattern Matching
- Use Godog's Exact Patterns: Step regex must match Godog's suggestions precisely
- Use Exact Parameter Names: Godog expects
arg1, arg2, not descriptive names - Avoid Undefined Warnings: Even small deviations cause "undefined step" warnings
- Test Patterns First: Use
godog.ErrPendingto verify patterns work before implementing logic - Don't Over-Optimize Regex: Use the patterns Godog provides, even if they seem verbose
Critical Requirements from Validated Implementation
-
Godog has very specific requirements for step pattern matching:
- Use the exact regex pattern that Godog suggests in error messages
- Use the exact parameter names that Godog suggests (
arg1, arg2, etc.) - Match the feature file syntax exactly including quotes and JSON formatting
-
The "undefined" warnings are not a Godog bug - they occur when step definitions don't match Godog's expected patterns exactly:
- Using different regex patterns than what Godog suggests
- Using descriptive parameter names instead of
arg1, arg2 - Not escaping quotes properly in JSON patterns
- Trying to be "clever" with regex optimization
-
Solution: Always use the exact pattern and parameter names that Godog suggests in its error messages.
JSON Escaping
- Feature Files: Use double backslashes for quotes:
"{\\"message\\":\\"Hello\\"}" - Step Implementation: Trim surrounding quotes and backslashes from captured groups
- Response Validation: Trim trailing newlines from JSON responses
Server Verification
- Actual HTTP Requests:
theServerIsRunningmust make real HTTP call to/api/ready - No Mocking: Black box testing requires real server verification
- Port Conflicts: Test server runs on fixed port 9191
Context Handling
- ScenarioContext vs Context: Steps receive
*godog.ScenarioContext, notcontext.Context - Client Access: Store client in StepContext struct for step access
- Fresh Instances: Each scenario gets new client instance
Best Practices
Step Definition Patterns
// ✅ DO: Use Godog's exact regex patterns and parameter names
ctx.Step(`^I request a greeting for "([^"]*)"$`, sc.iRequestAGreetingFor)
ctx.Step(`^the response should be "{\"([^"]*)\":\"([^"]*)"}"$`, sc.theResponseShouldBe)
// ❌ DON'T: Use different parameter names or patterns
ctx.Step(`^I request greeting "(.*)"$`, sc.iRequestAGreetingFor) // Wrong pattern
ctx.Step(`^the response should be "{\"message\":\"([^"]*)"}"$`, sc.theResponseShouldBe) // Wrong pattern
Validated Step Definition Strategy
- First eliminate "undefined" warnings by using Godog's exact suggested patterns
- Return
godog.ErrPendinginitially to confirm pattern matching works - Then implement actual validation logic
- One pattern per step type - Use generic patterns to cover similar steps
Response Validation
// ✅ DO: Trim newlines and properly unescape JSON
func (c *Client) ExpectResponseBody(expected string) error {
actual := strings.TrimSuffix(string(c.lastBody), "\n")
if actual != expected {
return fmt.Errorf("expected %q, got %q", expected, actual)
}
return nil
}
// ❌ DON'T: Assume exact string matching without cleanup
func (c *Client) ExpectResponseBody(expected string) error {
if string(c.lastBody) != expected { // May fail due to newlines
return fmt.Errorf("mismatch")
}
}
Test Server Management
// ✅ DO: Use hybrid in-process testing
func (s *Server) Start() error {
// Start real server in same process
go func() {
if err := s.httpServer.ListenAndServe(); err != nil {
log.Error().Err(err).Msg("Test server failed")
}
}()
return s.waitForServerReady()
}
// ❌ DON'T: Use external process management
func startServer() {
// Avoid: cmd := exec.Command("go", "run", "./cmd/server")
}
Response Validation
// ✅ DO: Trim newlines and properly unescape JSON
func (c *Client) ExpectResponseBody(expected string) error {
actual := strings.TrimSuffix(string(c.lastBody), "\n")
if actual != expected {
return fmt.Errorf("expected %q, got %q", expected, actual)
}
return nil
}
// ❌ DON'T: Assume exact string matching without cleanup
func (c *Client) ExpectResponseBody(expected string) error {
if string(c.lastBody) != expected { // May fail due to newlines
return fmt.Errorf("mismatch")
}
}
Progressive Disclosure
Core Instructions (SKILL.md)
- BDD testing fundamentals
- Common workflows and patterns
- Gotchas and best practices
- Basic troubleshooting
Detailed Reference (references/)
- GODOG_PATTERNS.md: Advanced step pattern examples
- TEST_SERVER.md: Test server implementation details
- DEBUGGING.md: Advanced debugging techniques
- EXAMPLES.md: Complete feature examples
Scripts (scripts/)
- run-bdd-tests.sh: Test validation and execution
- debug-steps.sh: Step pattern debugging
- generate-stubs.sh: Step definition stub generation
Validation
Test Validation Script
# scripts/run-bdd-tests.sh
#!/bin/bash
set -e
echo "Running BDD tests..."
go test ./features/... -v
# Fail if any undefined/pending/skipped steps
echo "Validating test results..."
if go test ./features/... 2>&1 | grep -q "undefined\|pending\|skipped"; then
echo "ERROR: Found undefined, pending, or skipped steps"
exit 1
fi
echo "✓ All BDD tests passed with no undefined steps"
Common Validation Issues
| Issue | Cause | Solution |
|---|---|---|
| Undefined steps | Step pattern doesn't match Godog's exact regex | Use Godog's suggested pattern |
| JSON mismatch | Trailing newlines or improper escaping | Trim newlines, properly unescape JSON |
| Server not running | Test server failed to start | Check port 9191, verify server logs |
| Context errors | Wrong context type passed to steps | Use *godog.ScenarioContext, not context.Context |
References
Troubleshooting
"Undefined Step" Warnings
Symptoms: Tests pass but show "undefined step" warnings
Cause: Step regex doesn't match Godog's exact pattern suggestions
Solution:
- Run
godog --format=progressto see suggested patterns - Update step registration to use exact patterns
- Ensure function names match step descriptions
JSON Comparison Failures
Symptoms: Response validation fails despite correct JSON
Cause: Trailing newlines or improper escaping in feature files
Solution:
- Trim newlines:
strings.TrimSuffix(response, "\n") - Properly escape JSON in feature files:
"{\\"key\\":\\"value\\"}" - Trim quotes in step implementation:
strings.Trim(arg,"`)`
Server Connection Errors
Symptoms: "connection refused" or server not responding
Cause: Test server not running or port conflict
Solution:
- Verify server on port 9191:
curl http://localhost:9191/api/ready - Check server logs for startup errors
- Ensure no other process using port 9191
Context Type Mismatches
Symptoms: Compilation errors about context types
Cause: Passing wrong context type to step functions
Solution:
- Store
*godog.ScenarioContextin StepContext - Use stored context for step registration
- Access client through StepContext struct
Assets
- feature-template.feature: Gherkin feature file template
- step-template.go: Go step definition template
- test-server-template.go: Test server implementation template
- validation-script.sh: Test validation script template