以某种顺序运行 PHPUnit 测试

有没有办法让 TestCase内部的测试按照一定的顺序运行?例如,我想将一个对象的生命周期从创建到使用到销毁分离出来,但是我需要确保在运行其他测试之前首先设置了该对象。

39797 次浏览

也许您的测试中存在设计问题。

通常,每个测试不必依赖于任何其他测试,因此它们可以以任何顺序运行。

每个测试都需要实例化和销毁它需要运行的所有东西,这将是一个完美的方法,您永远不应该在测试之间共享对象和状态。

您能更具体地说明为什么 N 个测试需要同一个对象吗?

如果您的测试需要以某种顺序运行,那么它们确实存在问题。每个测试应该完全独立于其他测试: 它帮助您进行缺陷定位,并允许您获得可重复的(因此也是可调试的)结果。

查看 这个网站获得大量的想法/信息,关于如何以避免这类问题的方式分解测试。

如果希望测试共享各种帮助器对象和设置,可以使用 setUp()tearDown()添加到 sharedFixture属性。

PHPUnit 通过 @ 视情况而定注释支持测试依赖性。

下面是文档中的一个示例,其中测试将按照满足依赖关系的顺序运行,每个依赖测试将一个参数传递给下一个参数:

class StackTest extends PHPUnit_Framework_TestCase
{
public function testEmpty()
{
$stack = array();
$this->assertEmpty($stack);


return $stack;
}


/**
* @depends testEmpty
*/
public function testPush(array $stack)
{
array_push($stack, 'foo');
$this->assertEquals('foo', $stack[count($stack)-1]);
$this->assertNotEmpty($stack);


return $stack;
}


/**
* @depends testPush
*/
public function testPop(array $stack)
{
$this->assertEquals('foo', array_pop($stack));
$this->assertEmpty($stack);
}
}

但是,重要的是要注意,具有未解决的依赖项的测试将执行 没有(这是可取的,因为这将很快引起对失败测试的注意)。因此,在使用依赖关系时,密切关注是很重要的。

PHPUnit 允许使用“@视情况”注释,该注释指定依赖测试用例,并允许在依赖测试用例之间传递参数。

在我看来,采取下面的场景,我需要测试创建和销毁一个特定的资源。

最初我有两个方法,.testCreateResource 和 b.testDestroyResource

TestCreateResource

<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
?>

破坏资源

<?php
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>

我认为这是个坏主意,因为 testDestroyResource 依赖于 testCreateResource

TestCreateResource

<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
$app->deleteResource('resource');
?>

破坏资源

<?php
$app->createResource('resource');
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>

替代方案: 使用静电干扰函数来创建可重用的元素。例如(我使用 seleniumIDE 来记录测试,使用 phPunit-selenium (github)在浏览器中运行测试)

class LoginTest extends SeleniumClearTestCase
{
public function testAdminLogin()
{
self::adminLogin($this);
}


public function testLogout()
{
self::adminLogin($this);
self::logout($this);
}


public static function adminLogin($t)
{
self::login($t, 'john.smith@gmail.com', 'pAs$w0rd');
$t->assertEquals('John Smith', $t->getText('css=span.hidden-xs'));
}


// @source LoginTest.se
public static function login($t, $login, $pass)
{
$t->open('/');
$t->click("xpath=(//a[contains(text(),'Log In')])[2]");
$t->waitForPageToLoad('30000');
$t->type('name=email', $login);
$t->type('name=password', $pass);
$t->click("//button[@type='submit']");
$t->waitForPageToLoad('30000');
}


// @source LogoutTest.se
public static function logout($t)
{
$t->click('css=span.hidden-xs');
$t->click('link=Logout');
$t->waitForPageToLoad('30000');
$t->assertEquals('PANEL', $t->getText("xpath=(//a[contains(text(),'Panel')])[2]"));
}
}

好的,现在,我可以在其他测试中使用这个可重用的元素:)例如:

class ChangeBlogTitleTest extends SeleniumClearTestCase
{
public function testAddBlogTitle()
{
self::addBlogTitle($this,'I like my boobies');
self::cleanAddBlogTitle();
}


public static function addBlogTitle($t,$title) {
LoginTest::adminLogin($t);


$t->click('link=ChangeTitle');
...
$t->type('name=blog-title', $title);
LoginTest::logout($t);
LoginTest::login($t, 'paris@gmail.com','hilton');
$t->screenshot(); // take some photos :)
$t->assertEquals($title, $t->getText('...'));
}


public static function cleanAddBlogTitle() {
$lastTitle = BlogTitlesHistory::orderBy('id')->first();
$lastTitle->delete();
}
  • 通过这种方式,您可以构建测试的层次结构。
  • 您可以保留每个测试用例完全独立于其他用例的属性(如果您在每个测试之后清理 DB)。
  • 最重要的是,如果将来登录方式发生变化,您只需修改 LoginTest 类,并且在其他测试中不需要正确的登录部分(它们应该在更新 LoginTest 后工作) :)

当我运行测试我的脚本清理数据库的开始。上面我使用了我的 SeleniumClearTestCase类(我在那里制作了截图()和其他很好的函数) ,它是 MigrationToSelenium2的扩展(从 github,到使用 seleniumIDE + ff 插件“ Selenium IDE: PHP Formatter”在 firefox 中移植记录的测试) ,它是我的类 LaravelTestCase 的扩展(它是 Illumination Foundation TestCase 的副本,但不扩展 PHPUnit _ Framework _ TestCase) ,它设置 laravel 以便在我们想要在测试结束时清理数据库时可以访问雄辩) ,它是 PHPUnit _ Extended _ Selenium2TestCase 的扩展。为了建立 laravel 雄辩,我还在 SeleniumClearTestCase 函数 createApplication (在 setUp中调用,并从 laral test/TestCase 中获取此函数)中使用了这个函数

对于这个问题的正确答案是一个适合测试的配置文件。我也有同样的问题,并且通过创建带有必要的测试文件顺序的 testkit 来解决它:

phpunit.xml:


<phpunit
colors="true"
bootstrap="./tests/bootstrap.php"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
strict="true"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
stopOnRisky="false"
>
<testsuites>
<testsuite name="Your tests">
<file>file1</file> //this will be run before file2
<file>file2</file> //this depends on file1
</testsuite>
</testsuites>
</phpunit>