bartlett / phpunit-loggertestlistener
适用于兼容PSR-3日志记录器的PHPUnit测试套件监听器
Requires
- php: ^7.2|^8.0
- phpunit/phpunit: ^7.5|^8.0
- psr/log: ^1.0|^2.0|^3.0
Requires (Dev)
README
适用于兼容PSR-3日志记录器的PHPUnit测试套件监听器
目标是为PHPUnit提供一个标准的监听器,该监听器能够将单元测试结果发送到任何PSR-3兼容的日志记录器。
该监听器实现了PHPUnit\Framework\TestListener
接口,并使用以下日志级别/事件映射。
错误
- `addError()`, when an error occurred
- `addFailure()`, when a failure occured
警告
- `addWarning()`, on warning test
- `addIncompleteTest()`, on incomplete test
- `addRiskyTest()`, on risky test
- `addSkippedTest()`, when a test was skipped
信息
- `startTest()`, when a test started
- `endTest()`, when a test ended
通知
- `startTestSuite()`, when a test suite started
- `endTestSuite()`, when a test suite ended
安装
重要:为了演示目的,使用了Growl处理器和AdvancedFilter处理器,但它们目前还不是GitHub或Packagist版本。
composer require bartlett/phpunit-loggertestlistener
与基本PSR-3日志记录器一起使用
我们将使用一个非常基本的PSR-3日志记录器进行第一步。假设我们有以下实现。
<?php use Psr\Log\AbstractLogger; class YourLogger extends AbstractLogger { private $channel; public function __construct($name = 'YourLoggerChannel') { $this->channel = $name; } public function log($level, $message, array $context = array()) { error_log( sprintf( '%s.%s: %s', $this->channel, strtoupper($level), $this->interpolate($message, $context) ) ); } protected function interpolate($message, array $context = array()) { // build a replacement array with braces around the context keys $replace = array(); foreach ($context as $key => $val) { if (is_scalar($val)) { $replace['{' . $key . '}'] = $val; } } // interpolate replacement values into the message and return return strtr($message, $replace); } }
在您的phpunit.xml
配置文件中添加Bartlett\LoggerTestListener
测试监听器,使用我们的基本PSR-3日志记录器(YourLogger
)。
使用您日志记录器的默认行为
<?xml version="1.0" encoding="UTF-8"?> <phpunit> <listeners> <listener class="Bartlett\LoggerTestListener"> <arguments> <object class="YourLogger" /> </arguments> </listener> </listeners> </phpunit>
我们可以将通道名称(从默认的YourLoggerChannel
更改为值YourPSR3Logger
)更改如下
配置日志记录器的通道名称
<?xml version="1.0" encoding="UTF-8"?> <phpunit> <listeners> <listener class="Bartlett\LoggerTestListener"> <arguments> <object class="YourLogger"> <arguments> <string>YourPSR3Logger</string> </arguments> </object> </arguments> </listener> </listeners> </phpunit>
如果您想有更高级的日志记录策略,我建议您查看著名的Monolog PHP库。
与Monolog一起使用
没有处理器(然后默认输出将发送到STDERR),我们可以有如下配置,将日志记录器通道名称设置为YourMonologChannel
。
<?xml version="1.0" encoding="UTF-8"?> <phpunit> <listeners> <listener class="Bartlett\LoggerTestListener"> <arguments> <object class="Monolog\Logger"> <arguments> <string>YourMonologChannel</string> </arguments> </object> </arguments> </listener> </listeners> </phpunit>
添加一些处理器,使用基本的Monolog日志记录策略(仅按级别过滤)。
<?xml version="1.0" encoding="UTF-8"?> <phpunit> <listeners> <listener class="Bartlett\LoggerTestListener"> <arguments> <object class="Monolog\Logger"> <arguments> <string>YourMonologChannel</string> <array> <element> <object class="Monolog\Handler\StreamHandler"> <arguments> <string>/var/logs/monolog.log</string> </arguments> </object> </element> <element> <object class="Bartlett\GrowlHandler"> <arguments> <array></array> <integer>250</integer> <!-- NOTICE --> </arguments> </object> </element> </array> </arguments> </object> </arguments> </listener> </listeners> </phpunit>
警告:当使用桌面通知(如growl)或由Pushover提供的移动通知时,您可能不希望接收所有日志记录,只想接收最重要的记录(错误、失败、测试套件结束)。
实际上Monolog无法做到这一点。因此,这是Pull Request添加过滤功能的原因。它现在作为一个独立包提供。请参阅Monolog Wiki页面和CallbackFilterHandler项目的页面
现在创建一个预先定义的日志记录器,其中包括我们想要的全部处理器和每个处理器的过滤规则。
<?php use Monolog\Logger; use Monolog\Handler\StreamHandler; use Bartlett\Monolog\Handler\GrowlHandler; use Bartlett\Monolog\Handler\CallbackFilterHandler; class YourMonolog extends Logger { public function __construct($name = 'PHPUnit') { $filter1 = function($record, $handlerLevel) { if ($record['level'] < $handlerLevel) { return false; } if ($record['level'] > $handlerLevel) { return true; } return ( preg_match('/^TestSuite(.*)ended\./', $record['message']) === 1 and $record['level'] == $handlerLevel ); }; $stream = new StreamHandler('/var/logs/monolog.log'); $handlers = [$stream]; try { $growl = new GrowlHandler(array(), Logger::NOTICE); $filterGrowl = new CallbackFilterHandler( $growl, array($filter1) ); $handlers[] = $filterGrowl; } catch (\Exception $e) { // Growl client is probably not started echo $e->getMessage(), PHP_EOL, PHP_EOL; } parent::__construct($name, $handlers); } }
当然,我们像这样声明我们的新监听器
<?xml version="1.0" encoding="UTF-8"?> <phpunit> <listeners> <listener class="Bartlett\LoggerTestListener"> <arguments> <object class="YourMonolog" /> </arguments> </listener> </listeners> </phpunit>
我们将通过StreamHandler
记录所有PHPUnit日志消息,并通过GrowlHandler
(请参阅带有回调$filter1
的过滤规则)仅记录重要通知。
运行测试套件示例
phpunit -c examples/phpunit.monolog.xml
您将得到类似以下内容
PHPUnit 7.5.1 by Sebastian Bergmann and contributors. IRSF....F 9 / 9 (100%) Time: 79 ms, Memory: 10.00MB There were 2 failures: 1) Your\Name_Space\YourTestSuite::testFailure Failed asserting that an array is empty. /shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:31 2) Your\Name_Space\YourTestSuite::testDataProvider with data set #3 (1, 1, 3) Failed asserting that 2 matches expected 3. /shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:44 -- There was 1 risky test: 1) Your\Name_Space\YourTestSuite::testRisky This test did not perform any assertions /shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:20 FAILURES! Tests: 9, Assertions: 7, Failures: 2, Skipped: 1, Incomplete: 1, Risky: 1.
带有Monolog Stream处理器的测试套件结果
[2018-12-30 11:00:19] PHPUnit.NOTICE: TestSuite 'Demo Test Suite' started with 9 tests. {"suiteName":"Demo Test Suite","testCount":9,"operation":"startTestSuite"} [] [2018-12-30 11:00:19] PHPUnit.NOTICE: TestSuite 'Your\Name_Space\YourTestSuite' started with 9 tests. {"suiteName":"Your\\Name_Space\\YourTestSuite","testCount":9,"operation":"startTestSuite"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testIncomplete' started. {"testName":"testIncomplete","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testIncomplete"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testIncomplete","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.WARNING: Test 'testIncomplete' is incomplete. {"testName":"testIncomplete","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testIncomplete"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testIncomplete","operation":"addIncompleteTest","reason":"This test has not been implemented yet.","trace":"/shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:16\n"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testIncomplete' ended. {"testName":"testIncomplete","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testIncomplete"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testIncomplete","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testRisky' started. {"testName":"testRisky","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testRisky"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testRisky","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.WARNING: Test 'testRisky' is risky. {"testName":"testRisky","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testRisky"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testRisky","operation":"addRiskyTest","reason":"This test did not perform any assertions\n\n/shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:20","trace":""} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testRisky' ended. {"testName":"testRisky","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testRisky"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testRisky","operation":"endTest","output":"","assertionCount":0} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testSkipped' started. {"testName":"testSkipped","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testSkipped"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testSkipped","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.WARNING: Test 'testSkipped' has been skipped. {"testName":"testSkipped","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testSkipped"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testSkipped","operation":"addSkippedTest","reason":"This test was skipped for any reason.","trace":"/shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:26\n"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testSkipped' ended. {"testName":"testSkipped","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testSkipped"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testSkipped","operation":"endTest","output":"","assertionCount":0} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testFailure' started. {"testName":"testFailure","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testFailure"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testFailure","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.ERROR: Test 'testFailure' failed. {"testName":"testFailure","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testFailure"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testFailure","operation":"addFailure","reason":"Failed asserting that an array is empty.","trace":"/shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:31\n"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testFailure' ended. {"testName":"testFailure","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testFailure"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testFailure","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testPass' started. {"testName":"testPass","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testPass"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testPass","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testPass' ended. {"testName":"testPass","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testPass"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testPass","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.NOTICE: TestSuite 'Your\Name_Space\YourTestSuite::testDataProvider' started with 4 tests. {"suiteName":"Your\\Name_Space\\YourTestSuite::testDataProvider","testCount":4,"operation":"startTestSuite"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #0' started. {"testName":"testDataProvider with data set #0","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #0"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #0 (0, 0, 0)","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #0' ended. {"testName":"testDataProvider with data set #0","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #0"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #0 (0, 0, 0)","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #1' started. {"testName":"testDataProvider with data set #1","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #1"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #1 (0, 1, 1)","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #1' ended. {"testName":"testDataProvider with data set #1","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #1"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #1 (0, 1, 1)","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #2' started. {"testName":"testDataProvider with data set #2","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #2"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #2 (1, 0, 1)","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #2' ended. {"testName":"testDataProvider with data set #2","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #2"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #2 (1, 0, 1)","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #3' started. {"testName":"testDataProvider with data set #3","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #3"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #3 (1, 1, 3)","operation":"startTest"} [] [2018-12-30 11:00:19] PHPUnit.ERROR: Test 'testDataProvider with data set #3' failed. {"testName":"testDataProvider with data set #3","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #3"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #3 (1, 1, 3)","operation":"addFailure","reason":"Failed asserting that 2 matches expected 3.","trace":"/shared/httpd/phpunit-LoggerTestListener/examples/testSuite.php:44\n"} [] [2018-12-30 11:00:19] PHPUnit.INFO: Test 'testDataProvider with data set #3' ended. {"testName":"testDataProvider with data set #3","testDescriptionArr":["Your\\Name_Space\\YourTestSuite","testDataProvider with data set #3"],"testDescriptionStr":"Your\\Name_Space\\YourTestSuite::testDataProvider with data set #3 (1, 1, 3)","operation":"endTest","output":"","assertionCount":1} [] [2018-12-30 11:00:19] PHPUnit.NOTICE: TestSuite 'Your\Name_Space\YourTestSuite::testDataProvider' ended. {"suiteName":"Your\\Name_Space\\YourTestSuite::testDataProvider","testCount":3,"assertionCount":4,"failureCount":1,"errorCount":0,"incompleteCount":0,"skipCount":0,"riskyCount":0,"operation":"endTestSuite"} [] [2018-12-30 11:00:19] PHPUnit.NOTICE: TestSuite 'Your\Name_Space\YourTestSuite' ended. {"suiteName":"Your\\Name_Space\\YourTestSuite","testCount":1,"assertionCount":3,"failureCount":1,"errorCount":0,"incompleteCount":1,"skipCount":1,"riskyCount":1,"operation":"endTestSuite"} [] [2018-12-30 11:00:19] PHPUnit.NOTICE: TestSuite 'Demo Test Suite' ended. {"suiteName":"Demo Test Suite","testCount":4,"assertionCount":7,"failureCount":2,"errorCount":0,"incompleteCount":1,"skipCount":1,"riskyCount":1,"operation":"endTestSuite"} []
带有Growl处理器的测试套件结果
注意:由Growl for Windows(2.0.9和Smokestack显示)以及PHP GNTP库生成。
许可证
LoggerTestListener在BSD-3条款许可下授权 - 有关详细信息,请参阅LICENSE文件。