最近团队决定要统一使用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目录。

./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

4.支持测试覆盖率

这一块目前尚在研究中,虽然phpunit.xml中配置了覆盖率的相关配置,但是运行后还是提示:

Error: No code coverage driver is available

在sdebug的readme上有说明:

为了避免 swoole 的检测 xdebug 警告, 扩展注册的名称是 sdebug, 如果想使用 Phpunit CodeCoverage , 需要手动把检测 xdebug 的判断修改成 sdebug。

但是,具体如何做并没有给到详细的讲解。我们自己来搞定好了。

# 进入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这个函数的判断。我们再来找函数的定义位置:

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方法的判断。再次查找:

$ 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行为下面的代码即可。

return ($this->isPHP() || $this->isHHVM()) && (\extension_loaded('xdebug') || \extension_loaded('sdebug'));

然后,我们继续运行phpunit,会发现报下面的错误:

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行位置代码如下:

if (!\extension_loaded('xdebug')) {
throw new RuntimeException('This driver requires Xdebug');
}

同理,修改第37行代码即可:

if (!(\extension_loaded('xdebug') || \extension_loaded('sdebug'))) {

然后再次运行phpunit,终于可以运行完成测试覆盖率的生成了。
coverage-success.png

coverage.png
至此,我们终于能宣告完美支持hyperf的单元测试了。

标签: phpunit, hyperf

添加新评论