Installation
composer require --dev phpspec/phpspec
Note: The package is now maintained at phpspec/phpspec, but the core usage remains identical.
Initialize a Project
vendor/bin/phpspec init
This generates a basic phpspec.yml config file and example specs in spec/.
First Use Case: Writing a Spec
Create a spec file (e.g., spec/ExampleSpec.php) mirroring your class structure:
namespace spec\App;
use PhpSpec\ObjectBehavior;
use App\Example;
class ExampleSpec extends ObjectBehavior
{
public function it_is_initializable()
{
$this->shouldHaveType(Example::class);
}
}
Run it with:
vendor/bin/phpspec run
Key Directories
spec/: Contains all spec files (auto-discovered).phpspec.yml: Configuration (e.g., suites, extensions, formatter).BDD-First Development
Write specs before implementation. Use it() methods to describe behavior:
public function it_adds_two_numbers()
{
$this->add(2, 3)->shouldReturn(5);
}
Run specs incrementally:
vendor/bin/phpspec run --stop-on-failure
Mocking Dependencies
Use addMock() or beConstructedWith() for external services:
public function it_fetches_data_from_api()
{
$mockApi = $this->getMockBuilder('App\ApiClient')->getMock();
$mockApi->method('fetch')->willReturn(['data']);
$this->beConstructedWith($mockApi);
$this->getData()->shouldReturn(['data']);
}
Data Providers
Parameterize specs with with():
public function it_returns_correct_discount($input, $expected)
{
$this->calculateDiscount($input)->shouldReturn($expected);
}
public function getMatchers()
{
return [
'return_correct_discount' => function ($input, $expected) {
return $this->calculateDiscount($input) === $expected;
}
];
}
Suites and Tags
Organize specs into suites in phpspec.yml:
suites:
unit:
namespace: spec\App\Unit
src_path: src/App
integration:
namespace: spec\App\Integration
src_path: src/App
Run a specific suite:
vendor/bin/phpspec run --suite=unit
Integration with Laravel
public function it_resolves_from_container()
{
$this->getContainer()->shouldHaveType('Illuminate\Container\Container');
}
DatabaseMigrations extension (requires phpspec/prophecy-phpunit):
extensions:
PhpSpec\Extension\CodeCoverageExtension:
format: [clover]
CI/CD Pipeline
Add to composer.json scripts:
"scripts": {
"test": "phpunit",
"spec": "phpspec run --format=pretty"
}
Run in CI:
composer spec
Code Coverage Generate reports with:
vendor/bin/phpspec run --format=phpunit --stop-on-failure --code-coverage
IDE Support
phpspec-watch).Legacy Code
PhpSpec\Exception\Example\SkippedException to skip unimplemented specs:
public function it_should_be_implemented()
{
throw new SkippedException('Not implemented yet');
}
Alpha State (Historical)
phpspec/phpspec), but old docs may reference phpspec2.FIRST ALPHA warnings in output; ignore them post-migration.Mocking Static Methods
partialMock() or refactor to dependency injection:
$this->getWrappedObject()->staticMethod()->shouldReturn('mocked');
Time-Based Specs
time() or DateTime specs may fail due to non-determinism.DateTime or use PhpSpec\Wrapper\DateTimeWrapper:
$this->getWrappedObject()->getDate()->shouldBeLike(new \DateTime('2023-01-01'));
Laravel’s Service Container
app() can be verbose.PhpSpec\ServiceContainer\Container extension:
extensions:
PhpSpec\ServiceContainer\Container:
container: '@'
Performance with Large Suites
--filter:
vendor/bin/phpspec run --filter="UserSpec"
Verbose Output Enable debug mode:
vendor/bin/phpspec run -vvv
Isolation Issues
before()/after():
public function before()
{
$this->resetStaticProperty('App\Example::$counter');
}
Prophecy Mismatches
Prophecy\Exception\Double\MethodNotFoundException.->shouldReceive()->once() to enforce expectations.Custom Formatters
phpspec.yml:
formatter:
name: pretty
# or
name: json
path: phpspec.json
Excluding Files
exclude:
- spec/ExampleSpec.php
Parallel Execution
php-parallel-lint for static analysis.Custom Matchers
Extend PhpSpec\Matcher\Matcher:
namespace spec\App\Matcher;
use PhpSpec\Matcher\Matcher;
class BeEven extends Matcher
{
public function match($value)
{
return $value % 2 === 0;
}
}
Register in phpspec.yml:
extensions:
App\Matcher\BeEven:
namespace: spec\App\Matcher
Hooks
Implement PhpSpec\Event\EventListener for pre/post-spec hooks:
public function beforeExample(ExampleEvent $event)
{
if ($event->getExample()->getDescription() === 'it_loads_data') {
$event->skip('Skipping DB-heavy spec');
}
}
Custom Loaders
Override PhpSpec\Loader\LoaderInterface to load specs from non-standard paths (e.g., JSON).
How can I help you explore Laravel packages today?