Specification-oriented BDD framework for PHP 8.2+. Describe behaviour. Drive design. Pair with AI. Now phpspec takes agile coding into the AI era.
$ phpspec pair > What's the smallest behavior we want to see in our string calculator? How about no argument means zero? + it('returns zero with no arguments', function () { + expect($this->calc->sum())->toBe(0); + }); Would you like me to create the spec? 1. Yes 2. No 3. Tell me what to do: > 1
phpspec 9 replaces the ObjectBehavior base class with a closure-based DSL
inspired by RSpec and Jasmine. No Prophecy. No magic methods. Just clear, expressive specs.
class CalculatorSpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType(Calculator::class); } function it_adds_two_numbers() { $this->add(2, 3)->shouldReturn(5); } }
describe(Calculator::class, function () { let('calculator', fn() => new Calculator()); it('is initializable', function () { expect($this->calculator) ->toBeAnInstanceOf(Calculator::class); }); it('adds two numbers', function () { expect($this->calculator->add(2, 3)) ->toBe(5); }); });
One framework. Zero heavy dependencies. Spec BDD and Story BDD unified.
Interactive pair REPL with tool-calling LLMs. next suggests what to spec next. refactor applies behaviour-preserving changes. Anthropic, OpenAI, and Google.
describe, context, it, let, expect — the full closure-based DSL with 30+ built-in matchers, negation, and chaining.
No Prophecy needed. mock(), allow()->toReturn(), toBeCalled()->once(). Argument matchers, type-hinted injection, and fluent call counts.
Write Gherkin .feature files with given/when/then step definitions. Background, Scenario Outline, DataTable, tags, and shared world.
Spec-first workflow: describe, exemplify commands. Auto-generate classes, interfaces, method stubs, and step definitions. --fake mode for instant green.
Pretty, Dot, TAP, JUnit formatters. Filter, random ordering, profiling, stop-on-failure, coverage reports, and quiet mode.
Built-in HTTP client: visit(), get(), post(). Response matchers for status, JSON paths, headers, and redirects.
Plug in custom matchers, formatters, commands, and event listeners. Composer-native packages with auto-discovery support.
--parallel[=N] runs specs across worker processes using Fibers. Result aggregation and ordered output.
A taste of what specs look like in phpspec 9.
$repo = mock(UserRepository::class); allow($repo->find(1))->toReturn('Alice'); allow($repo->find(2))->toReturn('Bob'); expect($repo->find(1))->toBe('Alice'); expect($repo->find(2))->toBe('Bob');
given('a greeting service', function () { $this->greeter = new 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); });
use App\UserService; use App\UserRepository; describe(UserService::class, function () { it('returns the user display name', function (UserRepository $repo) { allow($repo->find(1))->toReturn(['name' => 'Alice']); $service = new UserService($repo); expect($service->getDisplayName(1))->toBe('Alice'); }); it('returns Unknown for missing user', function (UserRepository $repo) { allow($repo->find(999))->toReturn(null); $service = new UserService($repo); expect($service->getDisplayName(999))->toBe('Unknown'); }); });