完美搞定hyperf的单元测试 作者: 灯小笼 时间: 2020-05-01 分类: 默认分类 最近团队决定要统一使用hyperf。工欲善其事,必先利其器。要提高开发效率,TDD必不可少,这就需要能有一套完美的单元测试支持工具。相比传统的php开发,hyperf基于swoole,需要满足其不同的特征,要想完美地搞定其单元测试,需要完成这么几件事情: 1. phpunit需要支持协程,才能正常运行下去 2. swoole需要支持openssl,才能支持对api接口进行单元测试 3. 支持单步跟踪。这点需要有xdebug的支持,同样,它也要支持协程 4. 支持单测覆盖率。 下面来分别介绍如何解决这几个问题。 ## 1. phpunit支持协程 在 Hyperf 里测试默认通过 phpunit 来实现,但由于 Hyperf 是一个协程框架,所以默认的 phpunit 并不能很好的工作,因此hyperf提供了一个 co-phpunit 脚本来进行适配,可以直接调用脚本或者使用对应的 composer 命令来运行。 在phpstorm里边,配置phpunit位置时,可以直接指定到项目的vendor/bin/co-phpunit即可。 ## 2. swoole支持openssl 这是因为在我们的业务代码中,会访问第三方的网站,需要支持通过https协议访问网站。 这需要在编译swoole插件的时候,通过`--enable-openssl`选项启用openssl的支持。另外,在mac通过brew安装的openssl,有可能不在默认位置,因此还需要通过选项`--with-openssl-dir`指定openssl目录。 ```bash ./configure --with-php-config=/usr/local/Cellar/php\@7.3/7.3.14/bin/php-config --enable-openssl --with-openssl-dir=/usr/local/Cellar/openssl\@1.1/1.1.1d/ ./configure ``` ## 3.支持单步跟踪 在swoole的官网文档,已经明确指出xdebug插件可能造成swoole的不稳定,不予支持。但也给出建议,可以使用sdebug (https://github.com/mabu233/sdebug) 作为替代品。 按照readme进行sdebug插件的安装即可。 ![hyperf-xdebug.jpg](https://c.dengxiaolong.com/blog/typecho/hyperf-xdebug.jpg-typecho) ## 4.支持测试覆盖率 这一块目前尚在研究中,虽然phpunit.xml中配置了覆盖率的相关配置,但是运行后还是提示: ```bash Error: No code coverage driver is available ``` 在sdebug的readme上有说明: > 为了避免 swoole 的检测 xdebug 警告, 扩展注册的名称是 sdebug, 如果想使用 Phpunit CodeCoverage , 需要手动把检测 xdebug 的判断修改成 sdebug。 但是,具体如何做并没有给到详细的讲解。我们自己来搞定好了。 ```bash # 进入vendor目录 $ cd vendor $ grep 'No code coverage driver is available' ./ -rn -C 2 grep 'No code coverage driver is available' ./ -rn -C 2 .//phpunit/phpunit/src/TextUI/TestRunner.php-484- .//phpunit/phpunit/src/TextUI/TestRunner.php-485- if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) { .//phpunit/phpunit/src/TextUI/TestRunner.php:486: $this->writeMessage('Error', 'No code coverage driver is available'); .//phpunit/phpunit/src/TextUI/TestRunner.php-487- .//phpunit/phpunit/src/TextUI/TestRunner.php-488- $codeCoverageReports = 0; ``` 由上述代码可知,关键还在于`canCollectCodeCoverage`这个函数的判断。我们再来找函数的定义位置: ```bash grep 'function canCollectCodeCoverage()' ./ -rn -A 4 .//sebastian/environment/src/Runtime.php:26: public function canCollectCodeCoverage(): bool .//sebastian/environment/src/Runtime.php-27- { .//sebastian/environment/src/Runtime.php-28- return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); .//sebastian/environment/src/Runtime.php-29- } .//sebastian/environment/src/Runtime.php-30- ``` 从上述代码可知,关键在于 `hasXdebug`方法的判断。再次查找: ```bash $ grep 'function hasXdebug()' ./ -rn -A 4 .//sebastian/environment/src/Runtime.php:174: public function hasXdebug(): bool .//sebastian/environment/src/Runtime.php-175- { .//sebastian/environment/src/Runtime.php-176- return ($this->isPHP() || $this->isHHVM()) && \extension_loaded('xdebug'); .//sebastian/environment/src/Runtime.php-177- } .//sebastian/environment/src/Runtime.php-178- ``` 这次,我们终于找到问题的源头了,修改`sebastian/environment/src/Runtime.php`文件的第176行为下面的代码即可。 ```php return ($this->isPHP() || $this->isHHVM()) && (\extension_loaded('xdebug') || \extension_loaded('sdebug')); ``` 然后,我们继续运行phpunit,会发现报下面的错误: ```bash PHP Fatal error: Uncaught SebastianBergmann\CodeCoverage\RuntimeException: This driver requires Xdebug in /Users/ronnie/PhpstormProjects/code/location-services/vendor/phpunit/php-code-coverage/src/Driver/Xdebug.php:38 ``` 打开该文件,发现第38行位置代码如下: ```php if (!\extension_loaded('xdebug')) { throw new RuntimeException('This driver requires Xdebug'); } ``` 同理,修改第37行代码即可: ```php if (!(\extension_loaded('xdebug') || \extension_loaded('sdebug'))) { ``` 然后再次运行phpunit,终于可以运行完成测试覆盖率的生成了。 ![coverage-success.png](https://c.dengxiaolong.com/blog/typecho/coverage-success.png-typecho) ![coverage.png](https://c.dengxiaolong.com/blog/typecho/coverage.png-typecho) 至此,我们终于能宣告完美支持hyperf的单元测试了。 标签: phpunit, hyperf