有没有一种方法可以使测试在TestCase内部以特定顺序运行? 例如,我想将对象的生命周期从创建到使用到销毁分开,但是我需要确保在运行其他测试之前首先设置了对象。
PHPUnit通过@depends批注支持测试依赖项。
这是文档中的一个示例,其中测试将按照满足依赖关系的顺序运行,每个依赖测试将参数传递给下一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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);
}
} |
但是,请务必注意,将不会执行具有未解决依赖性的测试(这是所希望的,因为这会很快引起失败测试的注意)。因此,在使用依赖项时必须密切注意。
您的测试中可能存在设计问题。
通常,每个测试都不得依赖任何其他测试,因此它们可以按任何顺序运行。
每个测试都需要实例化并销毁它需要运行的所有内容,这将是一种完美的方法,您永远不应在测试之间共享对象和状态。
您能否更具体地说明为什么需要N个测试使用同一对象?
正确的答案是用于测试的正确配置文件。我遇到了同样的问题,并通过使用必要的测试文件顺序创建测试套件来解决此问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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> |
如果希望测试共享各种辅助对象和设置,则可以使用setUp(),tearDown()添加到sharedFixture属性。
PHPUnit允许使用" @depends"注释,该注释指定相关的测试用例,并允许在相关的测试用例之间传递参数。
替代解决方案:
在测试中使用static(!)函数来创建可重用的元素。例如(我使用selenium IDE记录测试,并使用phpunit-selenium(github)在浏览器中运行测试)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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]"));
}
} |
好的,现在,我可以在其他测试中使用此可重用元素了:)例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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();
} |
-
这样,您可以构建测试的层次结构。
-
您可以保留每个测试用例彼此完全独立的属性(如果您在每次测试后清理数据库)。
-
最重要的是,例如,如果将来更改登录方式,则只需修改LoginTest类,而在其他测试中则不需要正确的登录部分(它们应在更新LoginTest之后工作):)
当我运行测试时,脚本会从头开始清理db ad。上面,我使用了我的SeleniumClearTestCase类(我在其中创建了screenshot()和其他漂亮的函数),它是MigrationToSelenium2的扩展名(从github,使用seleniumIDE + ff插件" Selenium IDE:PHP Formatters"在firefox中移植记录的测试)这是我的类LaravelTestCase的扩展(它是Illuminate Foundation Testing TestCase的副本,但没有扩展PHPUnit_Framework_TestCase),该类将laravel设置为在测试结束时希望清理数据库时能够雄辩地访问),这是PHPUnit_Extensions_Selenium2TestCase的扩展。为了设置雄辩的laravel,我还在SeleniumClearTestCase函数createApplication中(在setUp处调用了该函数,我从laral test / TestCase中获取了此函数)
我认为,在以下情况下,我需要测试特定资源的创建和销毁。
最初,我有两种方法。 testCreateResource和b。 testDestroyResource
一种。 testCreateResource
1 2 3 4
| <?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
?> |
b。 testDestroyResource
1 2 3 4
| <?php
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?> |
我认为这是个坏主意,因为testDestroyResource依赖于testCreateResource。更好的做法是
一种。 testCreateResource
1 2 3 4 5
| <?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
$app->deleteResource('resource');
?> |
b。 testDestroyResource
1 2 3 4 5
| <?php
$app->createResource('resource');
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?> |
如果测试需要按特定顺序运行,则确实存在问题。每个测试应该完全独立于其他测试:它可以帮助您进行缺陷定位,并允许您获得可重复(因此可调试)的结果。
在此站点上查看有关思想/信息的全部内容,以及有关如何以避免此类问题的方式考虑测试的因素。