Exécutez les Tests PHPUnit dans un certain ordre

Existe-t-il un moyen d'exécuter les tests dans un TestCase dans un certain ordre? Par exemple, je veux séparer le cycle de vie d'un objet de la création à l'utilisation à la destruction, mais je dois m'assurer que l'objet est configuré en premier avant d'exécuter les autres tests.

47
demandé sur reformed 2008-08-13 23:02:23

8 réponses

Peut-être qu'il y a un problème de conception dans vos tests.

Habituellement, chaque test ne doit pas dépendre d'autres tests, de sorte qu'ils peuvent s'exécuter dans n'importe quel ordre.

Chaque test doit instancier et détruire tout ce qu'il doit exécuter, ce serait l'approche parfaite, vous ne devriez jamais partager d'objets et d'États entre les tests.

Pouvez-vous être plus précis sur pourquoi vous avez besoin du même objet pour n tests?

47
répondu Fabio Gomes 2008-08-13 20:13:42

PHPUnit prend en charge les dépendances de test via l'annotation @depends.

Voici un exemple de la documentation où les tests seront exécutés dans un ordre qui satisfait les dépendances, chaque test dépendant passant un argument au suivant:

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

Cependant, il est important de noter que les tests avec des dépendances non résolues seront Pas exécutés (souhaitable, car cela attire rapidement l'attention sur le test défaillant). Donc, il est important de porter une attention particulière lors de l'utilisation dépendance.

125
répondu mjs 2014-12-17 22:50:08

Si vous souhaitez que vos tests partagent divers objets et paramètres d'assistance, vous pouvez utiliser setUp(), tearDown() à ajouter à la propriété sharedFixture.

8
répondu Gary Richardson 2008-08-23 22:30:48

La bonne réponse est un fichier de configuration approprié pour les tests. J'ai eu le même problème et l'ai corrigé en créant testsuite avec l'ordre des fichiers de test nécessaires:

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>
8
répondu Gino Pane 2017-07-26 10:09:27

PHPUnit permet l'utilisation de l'annotation '@depends' qui spécifie les cas de test dépendants et permet de passer des arguments entre les cas de test dépendants.

7
répondu saleem badreddine 2009-12-29 10:22:31

À mon avis, Prenez le scénario suivant où je dois tester la création et la destruction d'une ressource particulière.

Au départ, j'avais deux méthodes, a. testCreateResource et B. testdestroyresource

A. testCreateResource

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

B. testDestroyResource

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

Je pense que c'est une mauvaise idée, car testDestroyResource dépend de testCreateResource. Et une meilleure pratique serait de faire

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'));
?>
2
répondu bibstha 2010-03-28 10:42:21

Il y a vraiment un problème avec vos tests s'ils doivent s'exécuter dans un certain ordre. Chaque test doit être totalement indépendant des autres: il vous aide à localiser les défauts et vous permet d'obtenir des résultats répétables (et donc déboguables).

Checkout ce site pour un tas d'idées / informations, sur la façon de factoriser vos tests d'une manière où vous évitez ce genre de problèmes.

1
répondu jkp 2013-01-04 01:50:04

Solution Alternative: Utilisation statique(! les fonctions dans vos tests pour créer des éléments réutilisables. Par exemple (j'utilise selenium IDE pour enregistrer des tests et phpunit-selenium (github) pour exécuter le test dans le navigateur)

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

Ok, et maintenant, je peux utiliser ces éléments réutilisables dans d'autres tests :) par exemple:

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();
    }
  • de cette façon, vous pouvez construire une hiérarchie de vos tests.
  • vous pouvez conserver la propriété que chaque cas de test est totalement séparé des autres (si vous nettoyez DB après chaque test).
  • et le plus important, si par exemple, le mode de connexion change à l'avenir, vous ne modifiez que la classe LoginTest, et vous n'avez pas besoin d'une partie de connexion correcte dans d'autres tests (ils devraient fonctionner après la mise à jour LoginTest):)

Lorsque je lance tester mon script nettoyer DB ad le début. Ci-dessus j'utilise ma classe SeleniumClearTestCase (je fais screenshot () et d'autres bonnes fonctions là-bas) c'est l'extension de MigrationToSelenium2 (de github, au port des tests enregistrés dans firefox en utilisant seleniumide + ff plugin " Selenium IDE: PHP Formatters") qui est l'extension de ma classe LaravelTestCase (c'est une copie de Illuminate \ Foundation\Testing\TestCase mais n'étend pas PHPUnit_Framework_TestCase) qui configure laravel pour avoir accès à eloquent quand nous voulons nettoyer DB à la fin du test) qui est l'extension de PHPUnit_Extensions_Selenium2Testcase. Pour configurer laravel eloquent j'ai aussi dans SeleniumClearTestCase fonction createApplication (qui est appelée à setUp, et je prends cette fonction de laral test / TestCase)

1
répondu Kamil Kiełczewski 2016-06-17 21:12:40