在运行第一次测试之前设置PHPUnit并在运行最后一次测试后拆除

nai*_*vin 20 php phpunit

我正在尝试使用PHPUnit为php应用程序实现类似Django的测试实用程序.就像Django一样,我的意思是在运行第一个测试之前从主数据库创建一个单独的测试数据库,并在运行最后一个测试后删除它.即使一次运行多个测试用例,也只需创建一次测试数据库.

为此,我采取了以下方法 -

我定义了一个自定义测试套件类,以便我可以在其设置和拆卸方法中编写用于创建和删除db的代码,然后使用此类运行测试,如下所示

$ phpunit MyTestSuite
Run Code Online (Sandbox Code Playgroud)

MyTestSuite定义了一个名为的静态方法suite,我只是使用glob它并将测试添加到testsuite,如下所示

public static function suite() {
    $suite = new MyTestSuite();

    foreach (glob('./tests/*Test.php') as $tc) {
        require_once $tc;
        $suite->addTestSuite(basename($tc, '.php'));
    }

    return $suite;
}
Run Code Online (Sandbox Code Playgroud)

所有测试用例类都从子类延伸,PHPUnit_Framework_TestCase并且此类的setup和teardown方法负责从json fixture文件加载和清除初始数据.

现在作为没有.测试正在增加,我需要一次只运行一个选定的测试.但由于我已经使用测试套件加载测试,因此无法使用--filter选项.这让我觉得这种方法可能不正确.

所以我的问题是,在运行第一个测试之前和运行最后一个测试之后做什么的正确方法是什么,而不管PHPUnit如何找到它们?

PS:我没有使用PHPUnit_Extensions_Database_TestCase,而是我自己创建,填充和删除数据库的实现.

Ian*_*ter 35

我最近遇到了一些需要解决同一问题的问题.我用自定义类的方法尝试了Edorian的答案__destruct,但它似乎是在每个测试结束时运行而不是在所有测试结束时运行.

我没有在我的bootstrap.php文件中使用特殊的类,而是在我register_shutdown_function的所有测试结束后使用PHP的函数来处理数据库清理,它似乎完美无缺.

这是我在bootstrap.php文件中的一个例子

register_shutdown_function(function(){
   some_db_cleanup_methods();
});
Run Code Online (Sandbox Code Playgroud)

  • 一直在寻找这个大约 300 年,非常感谢! (2认同)

edo*_*ian 17

我的两个不自然的想法,不使用"Test Suites".一个是在底部.

测试听众

使用PHPUnits test listeners你可以做一个

  public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
  {
       if($suite->getName() == "yourDBTests") { // set up db
  }

  public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
  {
       if($suite->getName() == "yourDBTests") { // tear down db
  }
Run Code Online (Sandbox Code Playgroud)

您可以在xml配置文件中的testsuite中定义所有数据库测试,如图所示 in the docs

<phpunit>
  <testsuites>
    <testsuite name="db">
      <dir>/tests/db/</dir>
    </testsuite>
    <testsuite name="unit">
      <dir>/tests/unit/</dir>
    </testsuite>
  </testsuites>
</phpunit>
Run Code Online (Sandbox Code Playgroud)

引导

使用phpunits引导程序文件,您可以创建一个创建数据库的类,并__destruct在进程结束时将其分解为自己的方法.

将对象的引用放在某个全局范围内将确保对象仅在所有测试结束时被解构.(正如@beanland所指出的那样:使用register_shutdown_function()会更有意义!)


使用测试套件:

http://www.phpunit.de/manual/3.2/en/organizing-test-suites.html显示:

<?php

class MySuite extends PHPUnit_Framework_TestSuite
{
    public static function suite()
    {
        return new MySuite('MyTest');
    }

    protected function setUp()
    {
        print "\nMySuite::setUp()";
    }

    protected function tearDown()
    {
        print "\nMySuite::tearDown()";
    }
}

class MyTest extends PHPUnit_Framework_TestCase
{
    public function testWorks() {
        $this->assertTrue(true);
    }
}
Run Code Online (Sandbox Code Playgroud)

这在PHPUnit 3.6中运行良好,可以在3.7中使用.它不在当前的文档中,因为"测试套件类"有些不赞成/不鼓励,但它们将会存在很长一段时间.


请注意,拆除和设置每个测试用例的整个数据库对于对抗测试间依赖性非常有用,但如果不在内存中运行测试(如sqlite内存),速度可能不值得.


Tot*_*her 6

2020 年,@edorian 的方式已被弃用

/**
 * @throws Exception
 *
 * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039
 */
public function testSuiteLoaderClass(): string
{
    ///
}
Run Code Online (Sandbox Code Playgroud)

来源

新的解决方案

现在我们需要通过扩展来使用TestRunner。添加此代码:phpunit.xml

<extensions>
    <extension class="Tests\Extensions\Boot"/>
</extensions>
<testsuites>
  ...
</testsuites>
Run Code Online (Sandbox Code Playgroud)

Tests/Extensions/Boot.php:

<?php

namespace Tests\Extensions;

use PHPUnit\Runner\AfterLastTestHook;
use PHPUnit\Runner\BeforeFirstTestHook;

class Boot implements BeforeFirstTestHook, AfterLastTestHook
{
    public function executeBeforeFirstTest(): void
    {
        // phpunit --testsuite Unit
        echo sprintf("testsuite: %s\n", $this->getPhpUnitParam("testsuite"));

        // phpunit --filter CreateCompanyTest
        echo sprintf("filter: %s\n", $this->getPhpUnitParam("filter"));

        echo "TODO: Implement executeBeforeFirstTest() method.";
    }

    public function executeAfterLastTest(): void
    {
        // TODO: Implement executeAfterLastTest() method.
    }

    /**
     * @return string|null
     */
    protected function getPhpUnitParam(string $paramName): ?string
    {
        global $argv;
        $k = array_search("--$paramName", $argv);
        if (!$k) return null;
        return $argv[$k + 1];
    }
}
Run Code Online (Sandbox Code Playgroud)

执行

纯PHP:

phpunit --testsuite Unit --filter CreateCompanyTest
Run Code Online (Sandbox Code Playgroud)

拉拉维尔:

php artisan test --testsuite Unit --filter CreateCompanyTest
Run Code Online (Sandbox Code Playgroud)

输出:

PHPUnit 9.3.10 by Sebastian Bergmann and contributors.

testsuite: Unit
filter: CreateCompanyTest
TODO: Implement executeBeforeFirstTest() method.
Run Code Online (Sandbox Code Playgroud)