首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用PHPUnit对并发读写进行单元测试?

如何使用PHPUnit对并发读写进行单元测试?
EN

Stack Overflow用户
提问于 2016-01-26 22:48:37
回答 1查看 2.8K关注 0票数 23

我最近在一个实时应用程序上遇到了一个问题。我意识到数据库的并发异常和锁越来越多。

基本上,我启动了一个事务,它需要同一个表上的一个SELECT和一个INSERT来提交。

但是因为负载很重,每个事务都会锁定表,在大多数情况下,它是如此之快,以至于不会造成任何问题,但是有一个点,锁开始等待的时间越来越长。

通过调整查询,我在一定程度上解决了这个问题。

不过,现在,我想用PHPUnit编写一些测试,以验证我的修复并避免任何回归。

我找不到任何关于如何做到这一点的材料。

因为PHP不是多线程的,所以我不知道如何在一个测试中运行并发查询来进行验证。

基本上,我希望能够在一次测试中运行多个调用,以确保一切正常。

我知道我可以尝试通过直接查询http服务器并加载整个应用程序来做一些高级别的测试,但是由于我的问题来自于一个独立库,所以我更愿意对它本身进行测试。

有什么想法吗?

EN

回答 1

Stack Overflow用户

发布于 2017-05-02 23:23:42

简短的答案是,没有好的方法来测试使用PHPUnit在实际数据库上的并发读/写。对于这项工作,它根本不是合适的工具。

但是有几个方面的好的解决方案来测试这一点。首先,可以(也应该)编写代码来处理所有可能的问题。像PostgreSQL这样的数据库系统会在锁和事务问题上立即失败。为了优雅地处理它,我使用了类似如下的代码(伪代码,也用于回答another question):

代码语言:javascript
复制
begin transaction
while not successful and count < 5
    try 
        execute sql
        commit
    except
        if error code is '40P01' or '55P03'
            # Deadlock or lock not available
            sleep a random time (200 ms to 1 sec) * number of retries
        else if error code is '40001' or '25P02'
            # "In failed sql transaction" or serialized transaction failure
            rollback
            sleep a random time (200 ms to 1 sec) * number of retries
            begin transaction
        else if error message is 'There is no active transaction'
            sleep a random time (200 ms to 1 sec) * number of retries
            begin transaction
    increment count

然后创建两组测试:一组应该确认代码正确地处理了情况(即单元测试)。另一组测试是针对环境的(即集成/功能测试)。

单元测试

我发现在连接到数据库的PHPUnit测试中很难重现这种情况,而且使用真正的数据库不适合进行真正的单元测试。相反,应该创建抛出各种数据库异常的PDO存根和单元测试。这证实了代码按预期工作,但是没有在任何真正的数据库上测试并发性。请记住,单元测试只是用来确认你的代码写得是否正确,而不是用来测试第三方软件。

代码语言:javascript
复制
$iterationCount = 0;
$db->runInTransaction(function() use (&$iterationCount) {
    $iterationCount++;
    if ($iterationCount === 1) {
        $exception = new PDOExceptionStub('Deadlock');
        $exception->setCode('40P01');
        throw $exception;
    }
});

// First time fails, second time succeeds
$this->assertEquals(2, $iterationCount, 'Expected 2 iterations of runInTransaction');

编写一套完整的测试,不连接到数据库,但确认逻辑。

集成测试

正如您已经发现的,PHPUnit根本不是执行负载测试的正确工具。它不适合任何比顺序单元和集成测试更复杂的测试。您可以同时运行多个PHPUnit实例,以增加数据库的负载。然而,我发现这超出了它的本意,而且它也不能帮助您监视数据库中的问题。因此,我认为没有办法绕过您希望避免的更高级别的测试。

但是,您的库可以在不运行完整应用程序的情况下进行测试。我会创建最简单的应用程序来测试它。它可以有一个或多个连接到数据库的CLI脚本。可以多次派生这些脚本,以将负载放到数据库上。或者使用该库创建一个简单的web页面,并使用许多负载测试应用程序中的任何一个来测试它。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35016634

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档