Product Decisions This Supports
- Improved Test Isolation & Speed: Enables in-memory test doubles (stubs/fakes) to replace database dependencies, drastically reducing test execution time and eliminating global fixture pollution. Supports a shift from slow, state-dependent tests to fast, isolated unit/integration tests.
- Precision in Test Fixtures: Aligns with Modelling by Example, allowing teams to define granular test contexts per scenario (e.g., "What happens when the API fails?" vs. "What happens with a paid invoice?").
- Dual Test Strategy: Complements existing Behat/Mink end-to-end tests with infrastructure tests (using
.real services) to validate real dependencies without duplicating logic. Reduces flakiness in CI pipelines.
- Build vs. Buy: Avoids reinventing test double infrastructure (e.g., custom Prophecy wrappers or mock builders). Lowers maintenance overhead compared to homegrown solutions.
- Roadmap for Test Automation: Enables scaling of automated tests by:
- Replacing slow database-driven tests with fast stubs/fakes.
- Supporting parallel test execution (stubs are process-isolated).
- Reducing merge conflicts in test fixtures.
- Use Cases:
- API/Service Layer Testing: Stub external APIs (e.g., payment gateways, third-party services) to test business logic without network calls.
- Repository/DAO Testing: Replace Doctrine repositories with in-memory stubs to test service layers without hitting a database.
- Behavior-Driven Development (BDD): Integrates seamlessly with Behat/Symfony2Extension for scenario-specific test doubles.
- Legacy System Refactoring: Isolate components of monolithic apps to enable incremental testing and modernization.
When to Consider This Package
-
Adopt When:
- Your Symfony/PHP project relies heavily on database fixtures or external services in tests, leading to slow, flaky, or globally coupled tests.
- You need to scale automated tests (e.g., for CI/CD) but hit bottlenecks due to test execution time or resource contention.
- Your team practices BDD/ATDD and wants to avoid universal fixtures in favor of scenario-specific test doubles.
- You’re maintaining a large codebase where test isolation is critical (e.g., shared state between tests causes failures).
- You want to separate unit/integration tests from end-to-end tests while keeping validation coverage for real dependencies.
-
Look Elsewhere If:
- Your project doesn’t use Symfony (this is a Symfony bundle; alternatives like PHPUnit’s native mocks or Mockery may suffice).
- You need advanced mocking features (e.g., partial mocks, method call tracking) beyond stubs/fakes—consider Mockery or PHPUnit’s mock builder.
- Your tests are already fast and isolated (e.g., using in-memory databases like SQLite or Dockerized services).
- You require modern PHP support (last release was 2016; check for forks or alternatives like Symfony’s Test Component).
- You’re using non-Symfony DI containers (e.g., Laravel’s container; alternatives like Laravel’s MockBuilder exist).
How to Pitch It (Stakeholders)
For Executives/Stakeholders:
*"This package lets us write faster, more reliable tests by replacing slow, shared database fixtures with lightweight, scenario-specific test doubles. Think of it like swapping out a clunky, shared car for a fleet of electric scooters—each test gets its own isolated ‘vehicle’ to run on, so they’re quicker, cheaper, and don’t block each other. For example, instead of resetting a database before every test (which takes minutes), we’d use in-memory stubs (milliseconds). This directly impacts:
- Developer velocity: Engineers spend less time debugging flaky tests.
- CI/CD efficiency: Tests run 10x faster, reducing feedback loops.
- Code quality: Isolated tests catch regressions earlier, especially in complex systems.
- Cost savings: Less reliance on expensive test infrastructure (e.g., staging databases).
It’s a low-risk investment—we’d only enable it for test environments, and we can still run end-to-end tests against real services when needed. The tradeoff? We trade a small upfront setup cost for long-term gains in test reliability and speed."*
For Engineering Teams:
*"This bundle automates test doubles for Symfony services using Prophecy, so we can:
- Stub services (e.g., API clients, repositories) to simulate responses without hitting real dependencies.
- Fake services with custom implementations for controlled testing.
- Integrate with Behat to define test doubles per scenario (no more global fixtures).
- Validate real services separately using
.real suffixes (e.g., github_client.real).
Why this over alternatives?
- No manual mock setup: Annotate services once, and the bundle handles the rest.
- Prophecy-powered: Get verification (e.g.,
shouldHaveBeenCalled()) out of the box.
- Symfony-native: Works with the DI container and Behat/Symfony2Extension.
Example workflow:
// Before: Slow, shared DB test
$invoice = $repo->findOneBy(['id' => 1]); // Hits DB!
// After: Fast, isolated stub
$repo->findOneBy(['id' => 1])->willReturn($fakeInvoice); // In-memory!
Tradeoffs:
- Last updated in 2016 (but core concepts are timeless; we can fork if needed).
- Requires Symfony (but saves time vs. rolling our own).
Next steps:
- Pilot: Try it in one module (e.g., API tests).
- Measure: Compare test execution time before/after.
- Expand: Roll out to Behat scenarios if successful."*
Key Ask: "Let’s allocate 2 dev days to prototype this for our slowest test suite—if it cuts test time by 50%, we’ll prioritize full adoption."