Quick Start
Create your first spec in under five minutes. This guide walks through the red-green cycle that makes phpspec tick.
1. Install phpspec
$ composer require --dev phpspec/phpspec
2. Generate a spec
Use the describe command to scaffold a spec file:
$ bin/phpspec describe App/Calculator
This creates spec/App/Calculator.spec.php:
<?php use App\Calculator; describe(Calculator::class, function() { let("calculator", fn() => new Calculator()); it("instantiates", fn() => expect($this->calculator)->toBeAnInstanceOf(Calculator::class) ); });
3. Run it — Red
$ bin/phpspec run
The class App\Calculator doesn’t exist yet. phpspec will ask:
Looks like you are trying to spec App\Calculator, a class that doesn't exist yet. Would you like me to generate that class for you? [y/n] y Class App\Calculator generated in src/App/Calculator.php
4. Add behaviour
Open spec/App/Calculator.spec.php and add an example:
describe(Calculator::class, function () { let('calculator', fn() => new Calculator()); it('adds two numbers', function () { expect($this->calculator->add(2, 3))->toBe(5); }); });
Run again — the add() method doesn’t exist. phpspec offers to generate the stub. Say y, then implement it:
namespace App; class Calculator { public function add(int $a, int $b): int { return $a + $b; } }
5. Run again — Green
Once you spec, you never go back! Calculator Calculator ✓ adds two numbers 1 spec 1 example (1 pass) Finished in 0.0031 seconds
Using expect()
Every assertion uses the expect() function followed by a matcher:
// Identity (===) expect($value)->toBe(5); // Negation expect($value)->not()->toBe(3); // String contains expect($greeting)->toContain('hello'); // Exceptions expect(fn() => $calc->divide(1, 0)) ->toThrow(\InvalidArgumentException::class);
The describe / context / it pattern
Nest context blocks to organise examples by scenario:
describe(Calculator::class, function () { context('when adding', function () { it('adds positive numbers', function () { expect((new Calculator())->add(2, 3))->toBe(5); }); }); context('when dividing', function () { it('throws on division by zero', function () { expect(fn() => (new Calculator())->divide(1, 0)) ->toThrow(\InvalidArgumentException::class); }); }); });
Sharing state with let
Use let() to define values that are re-evaluated fresh for each example:
describe(Calculator::class, function () { let('calculator', fn() => new Calculator()); it('adds', function () { expect($this->calculator->add(1, 1))->toBe(2); }); it('subtracts', function () { expect($this->calculator->subtract(5, 3))->toBe(2); }); });
Access let values via $this->name. Type-hinted parameters auto-inject mocks — see Mocking.