Testing
The test suite is PHPUnit 13, configured in phpunit.xml. Tests live in tests/ under Tomaj\Hermes\Test\. Risky tests and warnings fail the run.
A handler test does not need Redis. Call handle() with a Message, or put that message in DummyDriver and call Dispatcher::handle(). DummyDriver lives in tests/ and is not a class you install.
Commands
composer.json scripts are test (an alias of phpunit), phpunit, phpstan, phpcs, cs-fix, rector, rector-fix, infection, coverage, and check, which runs phpcs, phpstan, rector and test in that order. phpunit and test run without coverage, so they need neither Xdebug nor PCOV; coverage and infection need one of them.
composer install
composer phpunit
vendor/bin/phpunitCode style is PER Coding Style, checked by PHP-CS-Fixer in dry-run mode over src, tests, and examples. composer cs-fix applies the fixes:
composer phpcsPHPStan runs at level max with the settings in phpstan.neon: bleeding edge, phpstan-strict-rules, phpstan-deprecation-rules and phpstan-phpunit, plus checkUninitializedProperties and reportUnmatchedIgnoredErrors. There is no baseline and no ignored error, in src/ or in tests/.
composer phpstanRector checks src and tests against the PHP 8.4 set and the dead code, code quality, type declaration and early return sets in rector.php. composer rector only reports the changes; composer rector-fix applies them:
composer rectorCoverage
composer coverageIt runs PHPUnit with XDEBUG_MODE=coverage. Reports:
- HTML:
build/coverage/index.html - Clover:
build/logs/clover.xml
CI runs PHPUnit on PHP 8.4, PHP 8.4 with --prefer-lowest, and PHP 8.5. The 65% statement coverage gate runs on the PHP 8.4 job. That job also uploads the coverage-report artifact on pushes to master.
Mutation testing uses Infection with infection.json5. It fails when the MSI is below 85% or the covered code MSI is below 90%:
composer infectionAfter a successful CI run on master, the documentation workflow publishes that HTML under the site, at the coverage path. The documentation itself is the rest of https://tomaj.github.io/hermes/. The two outputs share one GitHub Pages site so a coverage deploy cannot replace the guide.
What the tests stand in for
Tomaj\Hermes\Test\Driver\DummyDriver implements DriverInterface in memory. HandleTest, EmitTest, and the shutdown tests use it so they do not need Redis. It is not part of src/ and not a driver you install.
DummyDriver::setupPriorityQueue() sorts keys numerically before wait() reverses them. That is why HandleTest::testPriorityProcessing() sees the higher priority first, which matches the Redis drivers from 6.0. On 5.x the Redis drivers did not sort. Issue #50 is the Redis behavior.
Driver tests that need a server mock the client (Redis, Predis\Client, SqsClient, the php-amqplib AbstractConnection). A green PHPUnit run does not require a local Redis, RabbitMQ, or AWS account.
CI workflows
| Workflow | When |
|---|---|
| CI | push and pull request: PHPUnit on PHP 8.4, 8.4 lowest, and 8.5, code style, PHPStan level max, composer audit, and Infection |
| CodeQL Security Analysis | push and pull request to master, and a weekly schedule |
| OpenSSF Scorecard | push and pull request to master, branch protection changes, and a weekly schedule |
| Deploy Documentation | diagram, link, and SVG checks on pull requests and pushes; Pages deploy after CI succeeds on master |
FAQ
How do I test a handler?
Call handle() directly with a new Message(...) and assert the result and side effects. No Redis or worker is needed. The welcome email tutorial has a complete PHPUnit test.
Can I install DummyDriver?
No. It lives in tests/ of this repository. Copy the idea, an in-memory DriverInterface, into your own test suite.
Do the Hermes tests need Redis or RabbitMQ?
No. Driver tests mock the clients, so composer phpunit runs without any server.
Is there a composer test script?
Yes. It runs PHPUnit, like composer phpunit. composer check runs code style, PHPStan, Rector and the tests.
Does DummyDriver order priorities like Redis?
Yes, from 6.0. Both poll the highest priority number first. On 5.x the Redis drivers used reverse registration order. See Priority.