Error Handling
Pest BDD provides detailed error messages to help you quickly identify and fix issues. This guide covers common errors and how to resolve them.
Step Not Found
When no step definition matches the step text:
StepNotFoundException: No step definition found for:
"Given a user exist"
Did you mean?
Given a user exists
Given a user {name} exists
To create this step, add:
#[Given('a user exist')]
public function aUserExist(): void
{
// TODO: Implement step
}Causes
Typo in step text
gherkinGiven a user exist # Missing 's'Missing step definition
php// Step not defined anywhereStep class not discovered
bash# Run to update classmap composer dump-autoload --optimizeWrong step type
gherkinWhen a user exists # Should be Given
Solutions
- Fix the typo in your feature file
- Create the missing step definition
- Ensure step class is in an autoloaded directory
- Run
composer dump-autoload --optimize
Parameter Resolution Error
When a step parameter can't be resolved:
ParameterResolutionException: Cannot resolve parameter $user in step:
"When the user logs in"
Parameter details:
Name: user
Type: App\Models\User
Position: 0
Resolution attempts:
✗ Alias 'user' not found in context
✗ Type 'App\Models\User' not found in context
✗ No extracted value for 'user'
✗ No default value
Available in context:
Types:
- App\Models\Order (from "Given an order exists")
- App\Models\Product (from "Given a product exists")
Aliases:
- buyer (App\Models\User)
- seller (App\Models\User)
Suggestion: Did you forget a Given step that creates a User?Causes
Missing prerequisite step
gherkin# Missing: Given a user exists When the user logs in # No User available!Alias mismatch
gherkinGiven a user exists as {admin} When the user logs in # Expects $user, but alias is 'admin'Type mismatch
phppublic function login(AdminUser $user): void // But only User was created, not AdminUser
Solutions
Add the missing Given step:
gherkinGiven a user exists When the user logs inMatch alias to parameter name:
gherkinGiven a user exists as {user} When the user logs inOr update step definition:
phppublic function login(User $admin): void // Match aliasReturn the correct type from Given step
Step Execution Error
When a step throws an exception:
StepExecutionException: Step failed during execution:
"Then the order should be confirmed"
Error: Call to a member function fresh() on null
Step definition:
Class: Tests\Assertions\Order\OrderAssertions
Method: orderShouldBeConfirmed
File: tests/Assertions/Order/OrderAssertions.php:45
Context at failure:
Parameters received:
- $order: App\Models\Order (id: 123)
Available in context:
- User (from "Given a user exists")
- Order (from "When the user places an order")
Stack trace:
#0 tests/Steps/OrderSteps.php(45): ...
#1 src/Runner/ScenarioRunner.php(89): ...Common Causes
Null reference
php#[Then('the order should be confirmed')] public function confirmed(Order $order): void { expect($order->fresh()->status)->toBe('confirmed'); // $order->fresh() might return null! }Assertion failure
phpexpect($order->status)->toBe('confirmed'); // Actual: 'pending'Database constraint
phpUser::create(['email' => 'duplicate@test.com']); // Unique constraint violation
Solutions
Add null checks:
php$fresh = $order->fresh(); expect($fresh)->not->toBeNull(); expect($fresh->status)->toBe('confirmed');Debug the actual value:
phpdump($order->status); // See actual value expect($order->status)->toBe('confirmed');Ensure proper test isolation
Pattern Matching Error
When the pattern doesn't match the expected text format:
PatternMatchException: Step text doesn't match expected format:
Step: "Given a user John exists"
Pattern: "a user {name} exists"
Expected format: a user "quoted string" exists
Example: Given a user "John" exists
The pattern expects a quoted string for {name} because
the parameter is typed as 'string'.Causes
Missing quotes for string
gherkinGiven a user John exists # Should be "John"Quotes for integer
gherkinGiven there are "5" items # Should be 5
Solutions
Follow the type conventions:
string→ use quotes:"value"int→ bare number:42float→ bare decimal:3.14bool→ keywords:true,false,yes,no
Factory Error
When factory creation fails:
FactoryExecutionException: Failed to create model from factory
Factory: Database\Factories\UserFactory
Method: withName
Error: SQLSTATE[23000]: Integrity constraint violation:
1062 Duplicate entry 'john@test.com' for key 'users.email_unique'
Suggested fix:
- Use unique email: User::factory()->create(['email' => $this->faker->unique()->email()])
- Or reset database between testsCauses
Database state pollution
- Previous test left data behind
Unique constraint violation
- Faker generated duplicate value
Missing migration
- Table doesn't exist
Solutions
- Use database transactions in tests
- Use
->unique()with Faker - Run migrations before tests
Debugging Tips
Enable Verbose Output
See step-by-step execution:
pest --bdd -vAdd Debug Output
Temporarily add dumps:
#[Given('a user {name} exists')]
public function userExists(string $name): User
{
dump("Creating user: $name");
$user = User::factory()->create(['name' => $name]);
dump("Created user ID: {$user->id}");
return $user;
}Check Context State
Inspect what's available:
#[When('I debug the context')]
public function debugContext(ScenarioContext $context): void
{
dump($context->getTypes());
dump($context->getAliases());
}Isolate the Failure
Run single scenario:
pest --bdd --filter="Scenario name"Check Step Discovery
Verify steps are found:
composer dump-autoload --optimize
# Then run testsError Reference
| Error | Likely Cause | Quick Fix |
|---|---|---|
| Step not found | Typo or missing step | Check spelling, add step |
| Parameter resolution | Missing Given step | Add prerequisite step |
| Type mismatch | Wrong return type | Return correct type |
| Pattern mismatch | Wrong value format | Use quotes/no quotes correctly |
| Factory error | DB constraint | Use unique values |
| Null reference | Object not created | Check Given steps |
Best Practices
Write Defensive Steps
#[Then('the user should be active')]
public function userActive(User $user): void
{
// Refresh from database
$fresh = $user->fresh();
// Verify it exists
expect($fresh)->not->toBeNull()
->and($fresh->is_active)->toBeTrue();
}Use Descriptive Assertions
// Better error messages
expect($order->status)
->toBe('confirmed', "Order {$order->id} should be confirmed");Test Prerequisites
#[Given('a confirmed order exists')]
public function confirmedOrder(): Order
{
$order = Order::factory()->confirmed()->create();
// Verify setup worked
expect($order->status)->toBe('confirmed');
return $order;
}