What is Pest BDD?
Pest BDD is a plugin for Pest PHP that brings Behavior-Driven Development (BDD) capabilities to your test suite. It allows you to write human-readable feature specifications in Gherkin syntax and bind them to PHP code using native PHP 8 attributes.
The BDD Philosophy
Behavior-Driven Development is a software development approach that emphasizes collaboration between developers, QA, and non-technical stakeholders. At its core, BDD uses a ubiquitous language that everyone can understand.
The Three Amigos
BDD encourages collaboration through:
- Business - Defines what the system should do
- Development - Implements the behavior
- Testing - Verifies the behavior works correctly
Feature files written in Gherkin serve as a common language that bridges these perspectives.
Key Concepts
Features
A feature represents a piece of functionality from the user's perspective:
Feature: User Registration
As a visitor
I want to create an account
So that I can access member featuresScenarios
Scenarios are concrete examples of how a feature should behave:
Scenario: Successful registration
Given I am on the registration page
When I fill in valid details
Then I should see a welcome messageSteps
Steps are the building blocks of scenarios. They follow the Given-When-Then pattern:
- Given - Sets up the initial context (Arrange)
- When - Describes the action taken (Act)
- Then - Verifies the expected outcome (Assert)
How Pest BDD Works
Pest BDD takes a unique approach compared to traditional BDD frameworks:
1. PHP 8 Attributes
Instead of annotations or configuration files, step definitions use native PHP 8 attributes:
#[Given('a user {name} exists')]
public function userExists(string $name): User
{
return User::factory()->create(['name' => $name]);
}2. Type-Based Parameter Matching
The type hint in your method signature determines how parameters are extracted from step text:
// Pattern: "user has {count} items"
// Text: "user has 5 items"
// Extracted: $count = 5 (as integer)
#[Given('user has {count} items')]
public function userHasItems(int $count): void
{
// $count is automatically cast to int
}3. Auto-Discovery
Pest BDD automatically discovers:
- Step definitions - Classes with
#[Given],#[When],#[Then]attributes - Feature files -
.featurefiles intests/Behaviors/
No configuration files needed.
4. Context Injection
Return values from steps are stored in a scenario context and automatically injected into subsequent steps:
#[Given('a user {name} exists')]
public function userExists(string $name): User
{
return User::factory()->create(['name' => $name]);
}
#[Then('the user should be active')]
public function userShouldBeActive(User $user): void
{
// $user is automatically injected from context
expect($user->is_active)->toBeTrue();
}When to Use BDD
BDD is particularly valuable when:
- Collaboration is key - Multiple stakeholders need to understand system behavior
- Complex business logic - Rules that benefit from concrete examples
- Living documentation - Tests that serve as up-to-date specifications
- Acceptance testing - Verifying features work from end to end
BDD vs Unit Testing
BDD and unit testing serve different purposes and complement each other:
| Aspect | BDD (Pest BDD) | Unit Testing (Pest) |
|---|---|---|
| Scope | Feature/behavior level | Function/method level |
| Audience | All stakeholders | Developers |
| Language | Natural language (Gherkin) | Code |
| Purpose | Acceptance criteria | Implementation correctness |
| Speed | Slower (integration) | Faster (isolated) |
The TDD Double Loop pattern shows how to effectively combine both approaches.