如何使用 PHP 单元测试避免竞争条件

Posted

技术标签:

【中文标题】如何使用 PHP 单元测试避免竞争条件【英文标题】:How to avoid race conditions with PHP Unit testing 【发布时间】:2018-07-02 08:43:13 【问题描述】:

让我们从证明我在这里面临的竞争条件开始:

失败的测试用例❌

phpunit --filter testMethod testlass path/to/testFile.php
PHPUnit 5.7.26 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

You should really fix these slow tests (>500ms)...
 1. 6441ms to run testClass::testMethod


Time: 6.49 seconds, Memory: 22.00MB

There was 1 failure:

1) ≈
Unable to find row in database table [orders] that matched attributes 
["collect_cash":104,"id":1728,"final_total":100].
Failed asserting that 0 is greater than 0.

project/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:24
path/to/testFile.php:78

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

这是相同的测试运行并成功,没有任何代码更改:

成功的测试用例✅

$ phpunit --filter method class path/to/testFile.php
PHPUnit 5.7.26 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

You should really fix these slow tests (>500ms)...
 1. 1631ms to run testClass::testMethod

Time: 1.68 seconds, Memory: 22.00MB

哪里出错了????

失败发生在检查数据库记录的地方:

    $this->shopperPut("/some/api/call")
        ->seeApiSuccess();

    $this->seeInDatabase('orders', [
        'collect_cash'  => $order->final_total + $chargeFare,
        'id'            => $order->id,
        'final_total'   => $order->final_total,

/some/api/call 中我们这样做

    .. some logic

    $order->save();

    .. more logic

    return response()->success(compact('order'));

我尝试过的

我尝试过放置睡眠命令.. 但这很麻烦,会减慢测试速度,最重要的是并不总是有效。

我还能做什么?

注意:这个问题与许多开发人员使用同一个数据库无关。这完全是在我的本地主机上,实际上我使用的是专门为单元测试制作的 testDatabase。

【问题讨论】:

【参考方案1】:

我有类似的测试没有竞争条件问题。

$response = $this->get('/some/api/call');

$response->assertSessionHas('order');

$this-assertDatabaseHas('orders', [
    'collect_cash'  => $order->final_total + $chargeFare,
    'id'            => $order->id,
    'final_total'   => $order->final_total,
]);

https://laravel.com/docs/5.5/database-testing#introduction

【讨论】:

很有趣..你能提供assertDatabaseHas的文档参考吗? 该死的人.. 这仅在 Laravel 5.5 中可用!我在 5.3...这是升级的另一个原因.. 但这还不够好。如果你告诉我在使用assertDatabaseHas之前你有竞争条件,我会更高兴。使用此方法解决了您的问题.. 现在您只是在告诉我:嘿,伙计,我没有比赛条件!对你有好处:) 我认为你有竞争条件是因为执行请求的方式,而不是数据库断言。类似send this request and check database even if the request have not finished

以上是关于如何使用 PHP 单元测试避免竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用程序代码库在 PHP 中编写单元测试?

如何使用 Angular 4 Jasmine 单元测试用例覆盖 IF/ELSE 条件

如何进行高效的Rails单元测试

如何使用php单元测试多组数据?

如何使用 Doctrine Fixture 加载测试数据库进行 PHP 单元测试?

使用闭包进行 PHP 单元测试