Features & Steps

phpspec 9 unifies Story BDD and Spec BDD. Write acceptance scenarios in Gherkin .feature files and implement them with step definitions — no Behat required.

Feature files

Create .feature files in a features/ directory using standard Gherkin syntax:

Feature: Greeting
  As a user
  I want to be greeted
  So that I feel welcome

  Scenario: Say hello
    Given a greeting service
    When I greet "World"
    Then I should see "Hello, World!"

Step definitions

Define steps in features/steps/*.steps.php:

<?php

given('a greeting service', function () {
    $this->greeter = new App\Greeter();
});

when('I greet {string}', function (string $name) {
    $this->result = $this->greeter->greet($name);
});

then('I should see {string}', function (string $expected) {
    expect($this->result)->toBe($expected);
});

DSL functions

FunctionPurpose
given(pattern, closure)Register a Given step
when(pattern, closure)Register a When step
then(pattern, closure)Register a Then step
step_and(pattern, closure)Register an And step
step_but(pattern, closure)Register a But step

All keywords register into the same step registry — the keyword is for readability only.

Step patterns

Patterns use placeholders that capture values from step text:

PlaceholderMatchesPHP type
{string}Quoted text "..."string
{int}Integer \d+int
{word}Single word \w+string
{*}Anything .+string
given('there are {int} cucumbers', function (int $count) {
    $this->count = $count;
});

when('I eat {int} cucumbers', function (int $eaten) {
    $this->count -= $eaten;
});

Shared world

Each scenario gets a fresh StepWorld object. Steps within a scenario share state via $this:

given('I store {string} as message', function (string $msg) {
    $this->message = $msg;
});

then('the message should be {string}', function (string $expected) {
    expect($this->message)->toBe($expected);
});

Background

Background steps run before each scenario in a feature:

Feature: Background example
  Background:
    Given a common setup

  Scenario: First
    Then the setup should be available

  Scenario: Second
    Then the setup should be available

Scenario Outline

Expand a scenario template with multiple data rows:

Scenario Outline: Eating
  Given there are <start> cucumbers
  When I eat <eat> cucumbers
  Then I should have <left> cucumbers

  Examples:
    | start | eat | left |
    | 12    | 5   | 7    |
    | 20    | 5   | 15   |

Data tables

Pass tabular data to a step:

Scenario: User list
  Given the following users:
    | name  | role  |
    | Alice | admin |
    | Bob   | user  |
  Then there should be 2 users
use PhpSpec\StoryBDD\DataTable;

given('the following users:', function (DataTable $table) {
    $this->users = $table->toArray();
    // [['name' => 'Alice', 'role' => 'admin'], ...]
});

then('there should be {int} users', function (int $count) {
    expect($this->users)->toHaveCount($count);
});

DataTable implements ArrayAccess, Iterator, and Countable. Use $table->asClass(User::class) to hydrate rows into objects.

Tags

Add @tag annotations to features and scenarios:

@smoke
Feature: Tagged feature
  @wip
  Scenario: Tagged scenario
    Given a step

Step states

StateMeaning
passedStep executed successfully
failedStep threw an exception or expectation failed
pendingStep calls pending()
undefinedNo matching step definition found
skippedSkipped because a prior step failed

Running features

$ bin/phpspec run features/                      # all features
$ bin/phpspec run features/greeting.feature        # single feature
$ bin/phpspec run --story                          # only features, skip specs
$ bin/phpspec run --all                             # specs + features

Step generation

When running features with undefined steps, phpspec offers to generate step definition stubs with pending() calls:

phpspec run features/
3 undefined steps in features/greeting.feature.
Generate step definitions? [Y/n] y

Generated step stubs in features/steps/greeting.steps.php

The BDD cycle

Story BDD and Spec BDD form a complete development cycle:

Feature (acceptance)
    ↓
Steps (implement step definitions)
    ↓
Specs (unit specs for each class)
    ↓
Classes (implement source code)
    ↓
Green (everything passes)
  1. Write a .feature file describing desired behaviour
  2. Run bin/phpspec run features/ — steps are undefined
  3. Generate step definitions, implement them
  4. Steps reference classes that don’t exist — phpspec generates specs and classes
  5. Switch to Spec BDD: write specs, generate classes, implement
  6. Return to features — steps now pass