Skip to content

Pest BDDBehavior-Driven Development for PestPHP

Zero-config, Laravel-native BDD testing with PHP attributes

Pest BDD Logo

Quick Example

gherkin
@e-commerce @api
Feature: Order Placement
  As a customer I want to place orders

  Background:
    Given a premium user "Jane" exists
    And the user has a verified email

  @smoke @critical
  Scenario: Successful order with discount
    Given a product "Mechanical Keyboard" priced at $149.99 exists
    When the user places an order
    And applies discount code "SAVE20"
    Then the order total should be $119.99
    And the user should receive a confirmation email

  @regression
  Scenario Outline: Quantity pricing
    Given a product "USB-C Cable" priced at $<price> exists
    When the user orders <qty> items
    Then the order total should be $<total>

    Examples:
      | price | qty | total  |
      | 19.99 | 2   | 39.98  |
      | 19.99 | 5   | 94.95  |
php
use TestFlowLabs\PestTestAttributes\Given;

class UserFactory extends Factory
{
    #[Given('a user exists')]
    #[Given('bir kullanıcı mevcut')]              // Multi-language
    public function definition(): array { /* ... */ }

    #[Given('a premium user {name} exists')]
    #[Given('premium kullanıcı {name} mevcut')]   // Multi-language
    public function premium(string $name): static
    {
        return $this->state(['name' => $name, 'tier' => 'premium']);
    }

    #[Given('the user has a verified email')]     // State chaining
    public function verified(): static
    {
        return $this->state(['email_verified_at' => now()]);
    }
}
php
use TestFlowLabs\PestTestAttributes\Given;

class ProductFactory extends Factory
{
    #[Given('a product {name} priced at ${price} exists')]
    public function withNameAndPrice(string $name, float $price): static
    {
        return $this->state(['name' => $name, 'price' => $price]);
    }
}
php
use TestFlowLabs\PestTestAttributes\When;

class OrderActions
{
    #[When('the user places an order')]
    public function placeOrder(User $user, Product $product): Order
    {
        // $user and $product auto-injected from Given steps
        return Order::create([
            'user_id' => $user->id,
            'product_id' => $product->id,
        ]);
    }

    #[When('applies discount code {code}')]
    public function applyDiscount(Order $order, string $code): void
    {
        $order->applyDiscount($code);
    }

    #[When('the user orders {qty} items')]
    public function orderQuantity(Order $order, int $qty): void
    {
        $order->update(['quantity' => $qty]);
    }
}
php
use TestFlowLabs\PestTestAttributes\Then;

class OrderAssertions
{
    #[Then('the order total should be ${expected}')]
    public function assertTotal(Order $order, float $expected): void
    {
        expect($order->total)->toBe($expected);
    }

    #[Then('the user should receive a confirmation email')]
    public function assertConfirmationEmail(User $user): void
    {
        Mail::assertSent(OrderConfirmation::class, fn ($mail) =>
            $mail->hasTo($user->email)
        );
    }
}
bash
$ pest --bdd --tags="@smoke"

Feature: Order Placement @e-commerce @api

   PASS  Scenario: Successful order with discount @smoke @critical
 Given a premium user "Jane" exists
 And the user has a verified email
 Given a product "Mechanical Keyboard" priced at $149.99 exists
 When the user places an order
 And applies discount code "SAVE20"
 Then the order total should be $119.99
 And the user should receive a confirmation email

1 scenario (1 passed)
Filtered by: @smoke
7 steps (7 passed)
Duration: 0.12s

Released under the MIT License.