PHPUnit 加速技巧分享

ETL
• 阅读 4158

PHPUnit 加速技巧分享

具备高效的测试一如编写高效的应用一样重要。作为开发者来说,迅速得知你刚编写的代码是否能够正常运行,能够让开发效率大大提升。接下来我们将会介绍一些可以快速实现的小技巧,让你的代码测试变得更快。

该示例测试套件有意地模拟更广泛的测试集合,并突出改进的可行性。真实情况下,效率的提升可能有所差异。

ParaTest

这个包 是一个用来运行你的测试套件的 PHPUnit 扩展。 和 PHPUnit 不一样的是它可以利用你的多核 CPU 来并行的运行测试用例。

你可以通过 composer 来将它作为一个开发依赖安装以后开始使用 ParaTest

composer require --dev brianium/paratest

现在我们就可以像调用 PHPUnit 一样来调用 ParaTest 了。它会自动的根据你机器 CPU 核心数来判断要启动多少个进程。

<img src="https://user-gold-cdn.xitu.io...;h=611&f=png&s=233798" class="rm-style">

上面,你可以看到在控制台中输出了运行测试用例启动了5个并行的进程。对比一下,下面用 PHPUnit 运行了同样的测试用例。

<img src="https://user-gold-cdn.xitu.io...;h=177&f=png&s=20131" class="rm-style">

1.49 秒 和 6.15秒 !

尽管 ParaTest 可以自己确定进程数,你也可以尝试设置进程数针对你的机器进行优化。使用  ---processes 选项,你可以增加或减少进程数,因为并不是进程数越多测试效果越好。

./vendor/bin/paratest --processes 6

警告: 使用 ParaTest 测试数据库前,需要考虑如何准备数据。如果使用 Lavarel 的  RefreshDatabase ,运行测试用例后会回滚或者迁移数据库来写入 。 相反的是,通过  DatabaseTransactions  跳过数据持久化, 这在运行测试期间不会尝试修改数据。

重试失败的测试

PHPUnit 有个非常方便的功能就是,允许你重新只运行上次测试中失败的测试.。如果你正在进行红绿复建风格的 TDD 开发,它将会加快你的开发周期。让我们从一个通过所有现存测试的测试套件来了解一下它这个功能。

<img src="https://user-gold-cdn.xitu.io...;h=162&f=png&s=13166" class="rm-style">

接下来,新增一个 red-green-refactor 测试模型的测试用例,预期失败:

<img src="https://user-gold-cdn.xitu.io...;h=151&f=png&s=12965" class="rm-style">

在更改代码库之后你认为新测试会通过,你想重新运行该测试套件以期能按预期运行。问题在于这个套件现在已经要花 1.3 秒的时间才能运行,随着测试代码量的增加,所需严重等待的时间也随之增加。

如果我们只能运行失败的测试,那不是很好? 非常幸运的是 PHPUnit v7.3添加了这样做的能力

<img src="https://user-gold-cdn.xitu.io...;h=151&f=png&s=11977" class="rm-style">

为了实现这个功能,请将 cacheResult =“true” 添加到 phpunit.xml配置中。 PHPUnit 会始终记住以前哪些测试失败了。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit cacheResult="true"
         backupGlobals="false"
         ...>

现在,当我们运行我们的测试单元时, PHPUnit 将记住哪些测试失败并使用以下选项让我们可以重新运行那些失败的测试单元。

./vendor/bin/phpunit --order-by=defects --stop-on-defect

我们不再需要等待整个测试单元运行,以查看我们试图解决的一个测试是否正在通过。

将缓存文件 .phpunit.result.cache 添加到 .gitignore 也是一个好主意,这样它就不会最终被提交到你的仓库里。

慢测试分组

PHPUnit 允许你用 @group 注解来将测试用例添加到不同的「分组」。如果你有一些测试用例尤其的慢的话,最好是将他们分到同一个组。

class MyTest extends TestCase
{
    public function test_that_is_fast()
    {
        $this->assertTrue(true);
    }

    /**
     * @group slow
     */
    public function test_that_is_slow()
    {
        sleep(10);

        $this->assertTrue(true);
    }

    /**
     * @group slow
     */
    public function test_that_is_slow_2_adrians_revenge()
    {
        sleep(10);

        $this->assertFalse(false);
    }
}

在这个例子中,我们有两个测试用例要运行10秒钟。 在我们开发周期内最后一件要做的事情就是运行测试用例,尤其是在做测试驱动开发的时候需要测试用例瞬时执行完成。

由于两个慢的测试用例都在同一个分组所以你可以通过 PHPUnit 的 --exclude-group 选项在某一次测试运行中来排除他们。

./vendor/bin/phpunit --exclude-group slow

这个命令将会运行你测试用例中除了 slow 分组的所有测试用例。 测试用例分组还有一个好处,比如说你需要将你所有的慢测试用例整理成文档以便后面再来优化他们。

然而在部署到生产环境前进行一些检查确保所有测试用例能通过,包括慢的测试用例。 设置一个 CI 管道来运行测试用例会是个不错的方法。

过滤测试

PHPUnit 有一个 --filter 选项,它接受一个模式来确定运行哪些测试。例如,如果您将所有测试配置命名空间 ,则可以通过指定命名空间来运行特定的测试子集。 以下命令仅在 Tests\Unit\Models 命名空间中运行测试并排除所有其他命令。

./vendor/bin/phpunit --filter 'Tests\\Unit\\Models'

--filter 选项是灵活的,允许通过 methodNameClass::methodName 进行过滤,甚至可以通过带有 /path/to/my/test.php 的文件路径进行过滤。您应该查看此选项的 PHPUnit docs 并查看更多的内容。

密码哈希次数

Laravel 默认使用 bcrypt 密码哈希算法,这种设计在系统资源上缓慢且昂贵。如果您的测试是验证用户密码,可以通过设置算法使用的次数来减少测试运行的时间,因为它执行的次数越多,所需的时间就越长。

如果你的应用程序与 laravel/laravelproject 中的最新更改保持同步,你会发现哈希次数的数量可以使用环境变量进行自定义。bcrypt 允许的最小次数已经设置为4,在 phpunit.xml file.

但是,如果您没有同步最新的更新,可以使用 Hash 门面在CreatesApplication trait 中设置它。

public function createApplication()
{
    $app = require __DIR__.'/../bootstrap/app.php';

    $app->make(Kernel::class)->bootstrap();

    // 设置 bcrypt 哈希次数...
    Hash::rounds(4);

    return $app;
}

内存数据库

利用内存数据库 SQLite ,是另一种加速测试的方式。 你可以通过在 phpunit.xml  配置文件里添加两个环境字段,来迅速开启它。

<php>
    ...
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
</php>

说明:尽管这样看上去很容易,你应该考虑生产环境数据库一致性问题。如果你在生产环境使用了 MySQL 数据库,你应该警惕引入不同数据库所带来的测试上的不同,比如 SQLite。我在这篇文章 my feature test suite setup 里描述了很多细节上的不同点。我认为相比通过提升一点速度带来的好处,保持生产环境一致更重要。

禁用 Xdebug

如果你平时用不到 Xdebug 的话,可以禁用掉它,因为它会降低 PHP 执行速度,导致测试用例运行缓慢。如果你日常使用它来调试的话,为了执行测试而禁用它可能不是一个好的选择 —— 但你始终要知道这一点当你关注测试用例执行速度时。

你可以在下面这个测试用例看到,一旦我们禁用了 Xdebug,执行速度将会有极大的提高。下面是这个测试用例在 Xdebug 启用时的执行情况:

<img src="https://user-gold-cdn.xitu.io...;h=151&f=png&s=11703" class="rm-style">

以及同样的测试用例在 Xdebug 禁用时的执行情况:

<img src="https://user-gold-cdn.xitu.io...;h=151&f=png&s=11374" class="rm-style">

修复测试速度过慢

当然我们最希望看到的段落是是:修复测试速度过慢!如果您正在努力确定哪些测试导致测试单元变慢时,您可能需要查看 PHPUnit Report 。它是一个开源工具,允许您通过生成如下所示的云可视化您的测试单元的性能,其中较大的气泡代表慢速测试。这将使您能够在单元中找到最慢的测试并逐步提高其性能。

PHPUnit 加速技巧分享

转自 PHP / Laravel 开发者社区 https://laravel-china.org/top...
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Souleigh ✨ Souleigh ✨
4年前
如何只使用CSS提升页面渲染速度
用户喜欢快速的Web应用。他们期望页面加载速度快,运行流畅。如果滚动时出现动画中断或延迟,用户很可能就会离开你的网站。作为一名开发者,你可以做很多事情来提升用户体验。本文主要介绍你可以用来提升页面渲染速度的4个CSS技巧。1\.Contentvisibility一般来说,大部分Web应用都有复杂的UI元素,并且它的扩展超出了用户在浏览器
Wesley13 Wesley13
3年前
MySQL总结(十一)子查询
!(https://oscimg.oschina.net/oscnet/upa344f41e81d3568e3310b5da00c57ced8ea.png)子查询1\.什么是子查询需求:查询开发部中有哪些员工selectfromemp;通
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Python编程规范修炼
“GuidovanRossum(吉多·范罗苏姆,Python创始人)说过,代码的阅读频率远高于编写代码的频率。毕竟,即使是在编写代码的时候,你也需要对代码进行反复阅读和调试,来确认代码能够按照期望运行。”接下来我们今天就为大家分享《Python编程规范:让你的代码脚下生风》第三弹《Google开源项目风
Stella981 Stella981
3年前
26种JavaScript优化技术
开发人员的生活总是在学习新事物。作为前端开发人员必须知道一些使我们的代码如何更优雅,工作更轻松的技巧。也许你已经进行了很长时间的JavaScript开发,但有时你可能没有使用不需要解决或编写一些额外代码即可解决问题的最新功能。这些技术可以帮助你编写干净且优化的JavaScript代码。1\.多个条件判断//lo
可莉 可莉
3年前
20个常用的JavaScript简写技巧
任何编程语言的简写技巧都能够帮助你编写更简练的代码,让你用更少的代码实现你的目标。让我们一个个来看看JavaScript的简写技巧吧。1\.声明变量//Longhandlet x;let y20;//Shorthandlet x,y20;2\.给多个变量赋值我们可以使用数组解构来在一行中给
Wesley13 Wesley13
3年前
Go单元测试编写的五个建议
测试驱动开发是保持高代码质量的好方法,同时保护自己免于回归,并向自己和其他人证明自己的代码完成了预期的工作。这里有五个技巧和窍门可以改善你的测试。把你的测试放在一个不同的包里Go坚持同一个文件夹中的文件属于同一个包,除了_test.go文件。将测试代码移出软件包,可以让您编写测试,就好像您是软件包的真正用户。你不能
【团队效率提升】Python-PyWebIO介绍
PyWebIO提供了一系列命令式的交互函数,能够让咱们用只用Python就可以编写Web应用,不需要编写前端页面和后端接口,让简易的UI开发效率大大提高。
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(