Есть ли способ заставить тесты внутри TestCase
выполняться в определенном порядке? Например, я хочу разделить жизненный цикл объекта от создания до использования и уничтожения, но мне нужно убедиться, что объект установлен первым, прежде чем я запущу другие тесты.
PHPUnit поддерживает испытательные зависимости через [@depends] (https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.test-dependencies) аннотация.
Вот пример из документации, где тесты будут запущены в заказе, который удовлетворяет зависимости с каждым зависимым тестом, передающим аргумент следующему:
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);
}
}
Однако it' s важный, чтобы отметить, что тесты с нерешенными зависимостями будут not быть выполненным (желательный, поскольку это привлекает внимание быстро к провальному тесту). Так, it' s важный, чтобы обратить пристальное внимание, используя зависимости.
Возможно, в ваших тестах есть проблема с дизайном.
Обычно каждый тест не должен зависеть от других тестов, поэтому они могут выполняться в любом порядке.
Каждый тест должен инстанцировать и уничтожить все, что ему нужно для запуска, это был бы идеальный подход, вы никогда не должны делиться объектами и состояниями между тестами.
Не могли бы вы уточнить, зачем вам нужен один и тот же объект для N тестов?
Правильный ответ для этого - надлежащий конфигурационный файл для тестов. Я имел ту же проблему и зафиксировал ее, создав testsuite с необходимым испытательным заказом файлов:
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' аннотация, которая определяет зависимые прецеденты и позволяет мимолетные споры между зависимыми прецедентами.
По моему мнению, возьмите следующий сценарий, где я должен проверить создание и разрушение конкретного ресурса.
Первоначально у меня было два метода, a. testCreateResource и b. testDestroyResource
a. testCreateResource
<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
?>
b. testDestroyResource
<?php
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>
Я думаю, что это - плохая идея, поскольку testDestroyResource зависит от testCreateResource. И лучшая практика должна была бы сделать
a. testCreateResource
<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
$app->deleteResource('resource');
?>
b. testDestroyResource
<?php
$app->createResource('resource');
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>
Альтернативное решение: Используйте статические (!) функции в своих тестах, чтобы создать элементы многократного использования. Например (я использую селен IDE, чтобы сделать запись тестов и phpunit-селена (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, '[email protected]', '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, '[email protected]','hilton');
$t->screenshot(); // take some photos :)
$t->assertEquals($title, $t->getText('...'));
}
public static function cleanAddBlogTitle() {
$lastTitle = BlogTitlesHistory::orderBy('id')->first();
$lastTitle->delete();
}
Когда я запускаю тест, мой сценарий очищает db объявление начало. Выше я использую свой класс 'SeleniumClearTestCase' (я делаю скриншот () и другие хорошие функции там), это - расширение 'MigrationToSelenium2' (от GitHub, чтобы держать зарегистрированные тесты в строевой стойке в Firefox, используя seleniumIDE + и следующие плагин " Селен IDE: PHP Formatters"), который является расширением моего класса LaravelTestCase (это - копия Illuminate\Foundation\Testing\TestCase, но не расширяет PHPUnit_Framework_TestCase), который установка laravel, чтобы иметь доступ к красноречивому, когда мы хотим убрать DB в конце теста), который является расширением PHPUnit_Extensions_Selenium2TestCase. Чтобы настроить laravel красноречивый, я имею также в функции SeleniumClearTestCase createApplication (который называют при 'установке', и я беру эту функцию от laral test/TestCase),
Если ваши тесты должны выполняться в определенном порядке, это действительно проблема. Каждый тест должен быть полностью независим от других: это поможет вам в локализации дефектов и позволит получить повторяемые (а значит, и отлаживаемые) результаты.
Посмотрите этот сайт, где вы найдете множество идей / информации о том, как выстроить тесты таким образом, чтобы избежать подобных проблем.