Сведения о вопросе

nikolya

20:52, 5th August, 2020

Теги

php   unit-testing   phpunit    

Выполните PHPUnit тестов в определенном порядке

Просмотров: 426   Ответов: 8

Есть ли способ заставить тесты внутри TestCase выполняться в определенном порядке? Например, я хочу отделить жизненный цикл объекта от создания для использования до уничтожения, но мне нужно убедиться, что объект настроен первым, прежде чем я выполню другие тесты.



  Сведения об ответе

LIZA

18:07, 14th August, 2020

PHPUnit поддерживает тестовые зависимости через аннотацию @depends .

Вот пример из документации, где тесты будут выполняться в порядке, удовлетворяющем зависимостям, причем каждый зависимый тест передает аргумент следующему:

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);
    }
}

Однако важно отметить, что тесты с неразрешенными зависимостями не будут выполняться (желательно, так как это быстро привлечет внимание к неудачному тесту). Поэтому при использовании зависимостей важно обращать на них самое пристальное внимание.


  Сведения об ответе

piter

05:37, 13th August, 2020

Возможно, в ваших тестах есть проблема с дизайном.

Обычно каждый тест не должен зависеть от каких-либо других тестов, поэтому они могут выполняться в любом порядке.

Каждый тест должен создавать экземпляры и уничтожать все, что ему нужно выполнить, что было бы идеальным подходом, вы никогда не должны делиться объектами и состояниями между тестами.

Не могли бы Вы уточнить, зачем вам нужен один и тот же объект для N тестов?


  Сведения об ответе

qwerty101

05:40, 13th August, 2020

Правильный ответ на этот вопрос-это правильный конфигурационный файл для тестов. У меня была та же проблема, и я исправил ее, создав 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>


  Сведения об ответе

P_S_S

22:56, 12th August, 2020

Если вы хотите, чтобы ваши тесты совместно использовали различные вспомогательные объекты и настройки , вы можете использовать setUp(), tearDown() для добавления в свойство sharedFixture .


  Сведения об ответе

baggs

09:42, 1st August, 2020

PHPUnit позволяет использовать аннотацию "@ depends", которая определяет зависимые тестовые случаи и позволяет передавать аргументы между зависимыми тестовыми случаями.


  Сведения об ответе

park

19:47, 1st August, 2020

На мой взгляд, возьмем следующий сценарий, где мне нужно протестировать создание и уничтожение конкретного ресурса.

Изначально у меня было два метода: a. testCreateResource и b. testDestroyResource

а. testCreateResource

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

б. testDestroyResource

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

Я думаю, что это плохая идея, так как testDestroyResource зависит от testCreateResource. И лучшей практикой было бы это сделать

а. testCreateResource

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

б. testDestroyResource

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


  Сведения об ответе

dump

14:42, 4th August, 2020

Альтернативное решение: Используйте статические(!) функции в ваших тестах для создания повторно используемых элементов. Например (я использую selenium IDE для записи тестов и 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) :)

Когда я запускаю тестовый скрипт clean up db ad The begining. Выше я использую свой класс SeleniumClearTestCase (я делаю screenshot() и другие приятные функции там) это расширение MigrationToSelenium2 (от github, чтобы перенести записанные тесты в firefox с помощью seleniumIDE + FF plugin "Selenium IDE: PHP Formatters"), которое является расширением моего класса LaravelTestCase (это копия Illuminate\Foundation\Testing\TestCase, но не расширяет PHPUnit_Framework_TestCase), который настраивает laravel, чтобы иметь доступ к eloquent, когда мы хотим очистить DB в конце теста), который является расширением PHPUnit_Extensions_Selenium2Testcase. Для настройки laravel eloquent у меня есть также в SeleniumClearTestCase функция createApplication (которая вызывается в setUp , и я беру эту функцию из laral test/TestCase)


  Сведения об ответе

repe

08:47, 4th August, 2020

Там действительно есть проблема с вашими тестами, если они должны выполняться в определенном порядке. Каждый тест должен быть полностью независим от других: он помогает вам с локализацией дефектов и позволяет получить повторяемые (и, следовательно, отлаживаемые) результаты.

Проверьте этот сайт для получения целого груза идей / информации, о том, как факторизовать свои тесты таким образом, чтобы избежать подобных проблем.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться