在使用 SQLite 数据库时并行化功能测试

Parallelize functional tests while using a SQLite database

我在我的 Symfony2 项目中添加了许多测试,但现在有 53 个测试和 176 个断言,大约需要一分钟。我正在努力减少这个时间,因为如果我启用代码覆盖率报告,它需要 15 分钟。

$ phpunit -c app/phpunit.xml.dist PHPUnit 4.3.5 by Sebastian Bergmann.
Configuration read from app/phpunit.xml.dist
.....................................................
Time: 59.1 seconds, Memory: 361.00Mb
OK (53 tests, 176 assertions)

我已经配置 LiipFunctionalTestBundle correctly to use a SQLite database in the test environment (this is recommended by LiipFunctionalTestBundle):

app/config/config_test.yml

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver:   pdo_sqlite
                path:     %kernel.cache_dir%/test.db

并且我添加了 DoctrineFixturesBundle 的固定装置。

如果两个或多个 phpunit 实例在同一个 SQLite 文件中写入和读取数据,现在我必须创建 tests suites or groups to launch several instances of PHPUnit. But I'm seeing a future problem: how can tests can be parallelized (for example with paratest)?

我可以 Pass a variable to PhpUnit 更改 kernel.cache_dir 值并为每个 phpunit 实例创建一个 cache 目录。但它不能从命令行完成,所以如果我选择这个解决方案,我将不得不创建几个 phpunit.xml.dist。我正在寻找更方便的解决方案。

我通过创建与测试文件一样多的缓存目录找到了解决方法。

我定义了 8 个测试套件以适应我的 CPU:

的 8 个内核
<!-- app/phpunit.xml.dist -->
<?xml version="1.0" encoding="UTF-8"?>

<phpunit
    backupGlobals = "false"
    …
>
    …
    <testsuites>
        <testsuite name="Group1">
            <file>../src/AcmeBundle/Tests/Command/CommandTest.php</file>
            <file>…</file>
        </testsuite>
        …
        <testsuite name="Group8">
            <file>../src/AcmeBundle/Tests/Controller/AdminControllerTest.php</file>
        </testsuite>
    </testsuites>
</phpunit>

我创建了一个 phpunit.sh 脚本,它使用 export(按照 David Harkness 的建议,以便为每个测试定义特定路径套房:

#!/bin/bash

function test() {
    testsuite=
    export CACHE_PATH="$testsuite"
    OUTPUT=$(phpunit -c app/phpunit.xml.dist --testsuite $testsuite)
    RETURN=$?
    if [ $RETURN -eq 0 ]
    then
        echo -e -n "3[01;32m●  OK\t3[00m 3[01;35m$testsuite3[00m"
        tail=$(echo "$OUTPUT" | tail -n 3)
        echo -e "\t\"$tail\"" | tr '\n' '\t' | tr -s '\t'
        echo ""
    else
        echo -e "3[01;31m❌  ERROR3[00m 3[01;35m$testsuite3[00m (3[01;34m$RETURN3[00m)3[00m"
        echo "-----"
        echo "$OUTPUT"
        echo "-----"
    fi
}

for testsuite in $(seq 1 8)
do
    tester "Group$testsuite" &
done

# http://unix.stackexchange.com/questions/231602/how-to-detect-that-all-the-child-processes-launched-in-a-script-ended/231608#231608
wait

echo "Done"

如果一切正常,成功的测试套件会以绿色标记显示,phpunit 的输出将被截断以保持较小的输出。如果有错误会显示出来。

然后我将其添加到 app/AppKernel。php:

/**
 * @see http://symfony.com/fr/doc/2.3/cookbook/configuration/override_dir_structure.html#surcharger-le-repertoire-cache
 */
public function getCacheDir()
{
    $cachePath = getenv('CACHE_PATH');

    if (
        ($this->environment == 'test')
        &&
        (! empty($cachePath))
    ) {
        return(parent::getCacheDir().'/'.$cachePath.'/');
    }

    # else

    return parent::getCacheDir();
}

此代码将告诉 Symfony 在缓存文件夹中创建一个子目录,这意味着 PHPUnit 的多个实例不会使用相同的 SQLite 文件。


虽然有效,但这个解决方案并不完美,并且有一些缺点:

  • 即使我只想更改 SQLite 数据库的路径,它也会重新创建整个缓存
  • 它将使用 space 并且需要时间为每个 rest 文件创建一个缓存,如果你不使用 RAM 作为缓存,我建议你避免这种情况(我的解决方案是删除 app/ 中缓存 并创建一个符号链接:ln -s /run/shm/project/cache cache,测试速度更快,因为缓存是在 RAM 中完成的,而不是在硬盘驱动器上)

有一个 bundle 可以促进与 symfony 的并行测试运行。它也可以与常规数据库一起使用,而不仅仅是 sqlite。不幸的是,它还会为每个测试文件显示一个 PHPUnit 结果。但它只会启动与你的处理器实际可以同时执行的一样多的并行测试,而且它似乎也解决了缓存问题。