feat(bridge): add Mockery bridge (testo/bridge-mockery) - #254
Conversation
Closes php-testo#41 Implements MockeryPlugin + MockeryInterceptor to call Mockery::close() in a try/finally after every test, ensuring mock expectations are always verified and the Mockery container is cleared between tests.
There was a problem hiding this comment.
Pull request overview
Adds a new testo/bridge-mockery package that integrates Mockery into Testo by registering a TestRunInterceptor intended to call Mockery::close() after each test, plus initial acceptance tests and root composer wiring.
Changes:
- Add
testo/bridge-mockeryto the monorepo (package composer.json, plugin, interceptor, changelog). - Wire the new bridge into the root repo dev dependencies / suggestions and test autoloading.
- Add initial acceptance tests and a dedicated suites definition for the bridge.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| composer.json | Adds the bridge as a dev dependency + suggestion, and adds test autoload mapping. |
| bridge/mockery/composer.json | Defines the new testo/bridge-mockery package and its dependencies. |
| bridge/mockery/src/MockeryPlugin.php | Plugin entry point registering the interceptor into the pipeline. |
| bridge/mockery/src/Internal/MockeryInterceptor.php | Interceptor intended to ensure Mockery::close() runs after each test. |
| bridge/mockery/tests/suites.php | Declares the acceptance test suite for the bridge package. |
| bridge/mockery/tests/Acceptance/MockeryBridgeTest.php | Acceptance tests demonstrating mock/spy usage and intended teardown behavior. |
| bridge/mockery/CHANGELOG.md | Adds initial changelog for version 0.1.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ests - register the package in release-please (config, version.json seed, split-publish tag) - add mirror README and close-prs workflow to match the other bridges - reduce CHANGELOG to a stub so release-please manages it - wire the acceptance suite into testo.php and register MockeryPlugin on it - strengthen the acceptance test: #[Covers] + assert the container is reset, and drop the risky spy test by adding a real assertion - align the composer `suggest` wording with the infection entry Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… as assertions - MockeryInterceptor records verified expectations (ExpectationFulfilled) and unmet ones (ExpectationFailed) on the current test, so a mock-only test is not Risky and an unmet expectation fails the test instead of aborting the pipeline - guard the process-global Mockery container across fiber suspensions with the same save/reset/restore dance as MessengerHub::scope - tune ExpectationsInterceptor order to ORDER_ASSERTIONS + 2000 so the Assert plugin reads the history after the bridge records into it - add Self / Feature / Stub tests covering mock+assert combinations and statuses - declare testo/assert, testo/codecov, testo/test as dev dependencies Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the failure path of the teardown: a stub that leaves an unmet expectation (Failed) is followed by one that checks the container is empty at its start (Passed). The latter only passes if close() cleared the container despite the failure — proving the interceptor both fails the right test and resets state for subsequent tests, not only when a test passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
The docs is there https://php-testo.github.io/docs/bridge/mockery |
|
Installed and wired up in a production Yii3 invoice application today. Setup was exactly as documented — one plugin registration in First real-world test suite — three scenarios for #[Test]
final class As4RetryEngineTest
{
public function detectMissingReceiptsReturnsZeroForEmptyQueue(): void
{
$repo = m::mock(As4MessageRepositoryInterface::class);
/** @var \Mockery\Expectation $e */
$e = $repo->shouldReceive('findAwaitingReceipts');
$e->once()->andReturn([]);
$engine = new As4RetryEngine($repo, m::spy(As4SenderInterface::class), ...);
Assert::same(0, $engine->detectMissingReceipts());
}
// ...
}One Psalm note worth documenting: The spy pattern for don't-care dependencies ( |
Closes #41
What this adds
A new
bridge/mockery/package (testo/bridge-mockery) that integrates Mockery into Testo.How it works
MockeryPluginregistersMockeryInterceptoras aTestRunInterceptor. The interceptor wraps every test in atry/finallyand callsMockery::close()after each one — whether it passed or failed. This ensures:Usage
Files
bridge/mockery/composer.jsontesto/bridge-mockery)bridge/mockery/src/MockeryPlugin.phpPluginConfigurator— entry point users registerbridge/mockery/src/Internal/MockeryInterceptor.phpTestRunInterceptor— callsMockery::close()infinallybridge/mockery/tests/Acceptance/MockeryBridgeTest.phpThe bridge follows the same structure and conventions as the existing
bridge/symfony-consoleandplugin/lifecyclepackages.