PHPUnit:如何创建一个用于类中所有测试一次的函数?


72

我有一个PHPUnit测试用例类(由一些测试功能组成)。我想编写一个oneTimeSetUp()针对该类中的所有测试被调用一次的函数(与标准setUp()函数不同,该函数针对该类中的每个测试被调用一次)。换句话说,我正在寻找与JUnit@BeforeClass注解等效的PHPUnit 。

oneTimeTearDown()功能相同的问题。

有可能在PHPUnit中这样做吗?


我完全理解有时需要这样做以提高性能。建议尽可能避免这种情况,以免在测试之间共享状态。
格雷格·K

2
@格雷格:我同意。不过,在某些情况下,最好对所有测试都初始化一次(例如,建立与数据库的连接)。
snakile 2011年

我尝试通过在数据映射器中模拟Zend_Db / PDO适配器来避免需要数据库服务器,然后在类生成的SQL上运行断言。我有时对功能/端到端测试不可避免。
格雷格·K

1
@Greg:我正在从事的测试是功能测试。他们测试最高水平的最终产品。
snakile 2011年

Answers:


109

看看setUpBeforeClass()第6 PHPUnit文档的。

一次您应该使用tearDown tearDownAfterClass();

这两个方法都应在您的类中定义为静态方法。


3
我正要写同样的东西...你的答案就是答案
Fabio

34
是否有任何非静态替代方案?
Martijn

1
不幸的是,@ Martijn目前在phpunit中没有针对该方法的方法。可以通过使用一些“已初始化”标志或setUp()中的延迟加载来覆盖此缺少的功能。在每次测试之前它仍然会执行,但是如果已经初始化了测试类,则不会执行任何操作。
KonradGałęzowski17年

3
对于setUp,使用带有标志的防护是initialized有效的,但不能tearDown这样做,因为您不知道哪个是最后的测试。
olidem '18

请注意,这仅在xml或参数中的processIsolation不为true时有效。因此,setupBeforeClass()是否还有其他选择?
apolinux

12

我以相同的问题来到此页面,但是所有课程都接受了公认的答案,对我而言,这不是正确的答案。

如果您像我一样,您的第一个“集成测试”是清除数据库,然后运行迁移。这样可以使自己处于所有测试的数据库基准。此时,我一直在不断更改迁移文件,因此设置基准确实是所有测试的一部分。

迁移需要一段时间,所以我不希望它在所有测试中都可以运行。

然后,我需要建立测试每个数据库的数据库。我需要编写一个订单测试,但是首先我需要创建一些产品并进行测试,然后再测试一个导入功能。

因此,我所做的事情超级简单,但在互联网上解释得并不好。我创建了一个简单的测试来设置数据库。然后在您的phpspec.xml文件中添加一个测试套件。

<testsuite name="Products">
    <file>tests/in/SystemSetupTest.php</file>
    <file>tests/in/ProductTest.php</file>
    <file>tests/in/ProductImportTest.php</file>
</testsuite>

并在SystemSetupTest.php中。

class SystemSetupTest extends ApiTester
{

    /** @test */
    function system_init()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        self::createEM(); //this has all the code to init the system...
    }
}

然后像这样执行它:

phpunit --testsuite产品

最终,它变得容易得多。这将允许我正确地建立我的系统。

另外,我正在使用laravel5。使用时,setUpBeforeClass()我会遇到自举问题,我确定可以解决,但是上面使用的方法很完美。


1
优于运行的测试,像这样:./artisan migrate --datebase CONNECTION_NAME && ./vendor/bin/phpunit。如果您使用Shell的命令历史记录,这没什么大不了的。
x-yuri

10

setUpBeforeClass() 如果所有测试实际上都包含在一个类中,则这是执行此操作的方法。

但是,您的问题有点暗示您可能会将测试类用作多个测试类的基类。在这种情况下,setUpBeforeClass将在每个之前运行。如果只想运行一次,则可以使用静态变量来保护它:

abstract class TestBase extends TestCase {

  protected static $initialized = FALSE;

  public function setUp() {
    if (!self::$initialized) {
      // Do something once here for _all_ test subclasses.
      self::$initialized = TRUE;
    }
  }

}

最后的选择可能是测试监听器


2
这很有帮助,但请记住将添加parent::setUp();为的第一行public function setUp()
瑞安

这是我在您提早两天前说的,您以某种方式获得了所有赞成票。我想知道为什么。
cprn '20

6

bootstrap选项可以在这些情况下使用。

您可以从命令行调用它

phpunit --bootstrap myBootstrap.php

或将其放在XML文件中,如下所示:

<phpunit bootstrap="myBootstrap.php">
...
</phpunit>

2
对于我而言,大多数时候,此属性包含vendor/autoload.php用于加载作曲家的依赖项。由于不能存在多个引导文件,因此此解决方案不太适合
克里斯(Chris)

2
@Chris您可以vendor/autoload.php从引导文件中进行请求。例如require __DIR__.'/../vendor/autoload.php';
大卫

4

扩展接受的答案:

的无setUpBeforeClass()tearDownAfterClass()@beforeClass@afterClass在对象上下文(静态方法)运行。您可以通过@before使用静态属性来保护任何代码来解决该限制,如下所示:

class MyTest extends PHPUnit\Framework\TestCase
{
    private static $ready = false;

    /**
     * @before
     */
    protected function firstSetUp()
    {
        if (static::$ready))
            return;

        /* your one time setUp here */

        static::$ready = true;
    }
}

@after但是,它不能用于,因为无法说出何时调用了最后一个测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.