
Symfony:test Doubles Mocking Skill
- 404 installs
- 190 repo stars
- Updated August 6, 2026
- makfly/superpowers-symfony
symfony:test-doubles-mocking is a Claude Code skill that creates PHPUnit and Prophecy test doubles in Symfony projects for developers who need isolated, deterministic unit tests without flaky external dependencies.
About
symfony:test-doubles-mocking is a testing skill from makfly/superpowers-symfony, part of a 5-skill testing suite alongside Pest TDD, PHPUnit TDD, functional WebTestCase flows, and Panther/Playwright E2E coverage. The skill guides a RED-GREEN-REFACTOR workflow: write a failing test, apply minimal doubles for mailers, HTTP clients, repositories, and SDKs, then expand coverage for edge cases. Outputs include a change trace, list of modified test files, executed commands, and notes on regression confidence. Agents assert observable outcomes—HTTP responses and state changes—rather than mock internals. Use symfony:test-doubles-mocking when Symfony integration tests are flaky in CI or when external services must be stubbed for fast, repeatable unit isolation.
- Mocks vs stubs vs spies
- Symfony DI test container overrides
- Interface-based fake services
- HTTP client mock responses
- Avoiding brittle integration coupling
Symfony:Test Doubles Mocking by the numbers
- 404 all-time installs (skills.sh)
- Ranked #657 of 2,189 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 11, 2026 (Skillselion catalog sync)
npx skills add https://github.com/makfly/superpowers-symfony --skill symfonytest-doubles-mockingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 404 |
|---|---|
| repo stars | ★ 190 |
| Last updated | August 6, 2026 |
| Repository | makfly/superpowers-symfony ↗ |
How do you mock Symfony services in PHPUnit?
Isolate Symfony units by replacing mailers, HTTP clients, repositories, and external SDKs with mocks, stubs, and spies in PHPUnit or Pest suites.
Who is it for?
Symfony developers stabilizing CI by mocking external mailers, HTTP clients, repositories, and third-party SDKs in unit tests.
Skip if: End-to-end browser testing or teams already running fully integrated tests without external dependencies.
When should I use this skill?
Symfony tests fail intermittently, depend on live APIs, or need mocks for mailers, HTTP clients, or repositories.
What you get
PHPUnit or Pest test doubles, RED-GREEN-REFACTOR trace, changed test files list, and regression coverage notes.
- Mock and stub test classes
- RED-GREEN-REFACTOR execution trace
- Stabilized test file diffs
By the numbers
- Parent plugin includes 5 testing skills
- superpowers-symfony ships 47 total skills
Files
Test Doubles Mocking (Symfony)
Use when
- Building regression-safe behavior with TDD/functional/e2e tests.
- Converting bug reports into executable failing tests.
Default workflow
1. Write failing test for target behavior and one boundary case. 2. Implement minimal code to pass. 3. Refactor while preserving green suite. 4. Broaden coverage for invalid/unauthorized/not-found paths.
Guardrails
- Prefer deterministic fixtures/builders.
- Assert observable behavior, not internal implementation.
- Keep tests isolated and stable in CI.
Progressive disclosure
- Use this file for execution posture and risk controls.
- Open references when deep implementation details are needed.
Output contract
- RED/GREEN/REFACTOR trace.
- Test files changed and executed commands.
- Coverage and confidence notes.
References
reference.mddocs/complexity-tiers.md
Reference
Test Doubles and Mocking
Types of Test Doubles
| Type | Purpose |
|---|---|
| Dummy | Passed but never used |
| Stub | Returns predetermined values |
| Mock | Verifies interactions |
| Spy | Records calls for later verification |
| Fake | Working implementation (simplified) |
PHPUnit Mocks
Basic Mock
<?php
use App\Service\PaymentGateway;
use App\Service\OrderService;
use PHPUnit\Framework\TestCase;
class OrderServiceTest extends TestCase
{
public function testProcessPayment(): void
{
// Create mock
$gateway = $this->createMock(PaymentGateway::class);
// Configure return value
$gateway->method('charge')
->willReturn(new PaymentResult(success: true, transactionId: 'tx_123'));
$service = new OrderService($gateway);
$result = $service->processPayment(1000, 'EUR');
$this->assertTrue($result->isSuccessful());
}
}Mock with Expectations
public function testChargesCorrectAmount(): void
{
$gateway = $this->createMock(PaymentGateway::class);
// Expect specific call
$gateway->expects($this->once())
->method('charge')
->with(
$this->equalTo(1000),
$this->equalTo('EUR')
)
->willReturn(new PaymentResult(success: true));
$service = new OrderService($gateway);
$service->processPayment(1000, 'EUR');
}Consecutive Returns
public function testRetriesOnFailure(): void
{
$gateway = $this->createMock(PaymentGateway::class);
$gateway->expects($this->exactly(2))
->method('charge')
->willReturnOnConsecutiveCalls(
new PaymentResult(success: false), // First call fails
new PaymentResult(success: true) // Second succeeds
);
$service = new OrderService($gateway);
$result = $service->processPaymentWithRetry(1000, 'EUR');
$this->assertTrue($result->isSuccessful());
}Throwing Exceptions
public function testHandlesGatewayError(): void
{
$gateway = $this->createMock(PaymentGateway::class);
$gateway->method('charge')
->willThrowException(new GatewayException('Connection timeout'));
$service = new OrderService($gateway);
$this->expectException(PaymentFailedException::class);
$service->processPayment(1000, 'EUR');
}Callback for Complex Logic
public function testDynamicResponse(): void
{
$repository = $this->createMock(ProductRepository::class);
$repository->method('find')
->willReturnCallback(function (int $id) {
if ($id === 1) {
return new Product(id: 1, name: 'Product 1');
}
return null;
});
$service = new ProductService($repository);
$this->assertNotNull($service->getProduct(1));
$this->assertNull($service->getProduct(999));
}Prophecy (Alternative)
Prophecy provides a different syntax, often considered more readable.
<?php
use Prophecy\PhpUnit\ProphecyTrait;
class OrderServiceTest extends TestCase
{
use ProphecyTrait;
public function testProcessPayment(): void
{
// Create prophecy
$gateway = $this->prophesize(PaymentGateway::class);
// Stub method
$gateway->charge(1000, 'EUR')
->willReturn(new PaymentResult(success: true));
// Reveal to get actual mock
$service = new OrderService($gateway->reveal());
$result = $service->processPayment(1000, 'EUR');
$this->assertTrue($result->isSuccessful());
}
public function testCallsGatewayOnce(): void
{
$gateway = $this->prophesize(PaymentGateway::class);
// Expect call
$gateway->charge(1000, 'EUR')
->shouldBeCalledOnce()
->willReturn(new PaymentResult(success: true));
$service = new OrderService($gateway->reveal());
$service->processPayment(1000, 'EUR');
}
}Mocking Symfony Services
EntityManager
public function testPersistsEntity(): void
{
$em = $this->createMock(EntityManagerInterface::class);
$em->expects($this->once())
->method('persist')
->with($this->isInstanceOf(User::class));
$em->expects($this->once())
->method('flush');
$service = new UserService($em);
$service->createUser('test@example.com');
}Repository
public function testFindsUser(): void
{
$user = new User();
$user->setEmail('test@example.com');
$repository = $this->createMock(UserRepository::class);
$repository->method('findOneByEmail')
->with('test@example.com')
->willReturn($user);
$service = new UserService($repository);
$found = $service->findByEmail('test@example.com');
$this->assertSame($user, $found);
}MessageBus
public function testDispatchesMessage(): void
{
$bus = $this->createMock(MessageBusInterface::class);
$bus->expects($this->once())
->method('dispatch')
->with($this->callback(function ($message) {
return $message instanceof SendWelcomeEmail
&& $message->userId === 123;
}))
->willReturn(new Envelope(new \stdClass()));
$service = new RegistrationService($bus);
$service->register(123, 'test@example.com');
}Partial Mocks
Mock only some methods:
public function testPartialMock(): void
{
$service = $this->getMockBuilder(OrderService::class)
->setConstructorArgs([$this->gateway])
->onlyMethods(['sendNotification']) // Only mock this
->getMock();
$service->method('sendNotification')
->willReturn(true);
// Real processPayment, mocked sendNotification
$service->processPayment(1000, 'EUR');
}Fakes (Working Implementations)
<?php
// tests/Fake/InMemoryUserRepository.php
class InMemoryUserRepository implements UserRepositoryInterface
{
private array $users = [];
public function save(User $user): void
{
$this->users[$user->getId()] = $user;
}
public function find(int $id): ?User
{
return $this->users[$id] ?? null;
}
public function findByEmail(string $email): ?User
{
foreach ($this->users as $user) {
if ($user->getEmail() === $email) {
return $user;
}
}
return null;
}
}Usage:
public function testCreatesUser(): void
{
$repository = new InMemoryUserRepository();
$service = new UserService($repository);
$user = $service->createUser('test@example.com');
$this->assertNotNull($repository->findByEmail('test@example.com'));
}Best Practices
1. Mock dependencies, not the SUT: Don't mock the class you're testing 2. Use interfaces: Mock interfaces, not concrete classes 3. One mock assertion per test: Keep tests focused 4. Prefer stubs over mocks: Only verify when behavior matters 5. Use fakes for repositories: More realistic tests 6. Don't over-mock: Integration tests have value too
Skill Operating Checklist
Design checklist
- Confirm operation boundaries and invariants first.
- Minimize scope while preserving contract correctness.
- Test both happy path and negative path behavior.
Validation commands
- ./vendor/bin/phpunit --filter=...
- ./vendor/bin/phpunit
- ./vendor/bin/pest --filter=...
Failure modes to test
- Invalid payload or forbidden actor.
- Boundary values / not-found cases.
- Retry or partial-failure behavior for async flows.
Related skills
How it compares
Use symfony:test-doubles-mocking for unit-level doubles; choose symfony:functional-tests or symfony:e2e-panther-playwright for HTTP or browser coverage.
FAQ
Does symfony:test-doubles-mocking support Pest or only PHPUnit?
symfony:test-doubles-mocking focuses on PHPUnit mocks and Prophecy for isolated Symfony unit testing; the parent superpowers-symfony repo also ships a separate symfony:tdd-with-pest skill for Pest PHP workflows.
What artifacts does symfony:test-doubles-mocking produce?
symfony:test-doubles-mocking delivers a RED-GREEN-REFACTOR trace, a list of changed test files, executed test commands, and notes on regression coverage confidence after doubles are applied.