Question

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

Était-ce utile?

La solution

Il y a peut-être un problème de conception dans vos tests.

Habituellement, chaque test ne doit dépendre d’aucun autre test, ils peuvent donc être exécutés dans n’importe quel ordre.

Chaque test doit instancier et détruire tout ce dont il a besoin pour s'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 la raison pour laquelle vous avez besoin du même objet pour N tests ?

Autres conseils

PHPUnit prend en charge les dépendances de test via le @dépend annotation.

Voici un exemple tiré 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 pas être exécuté (souhaitable, car cela attire rapidement l'attention sur l'échec du test).Il est donc important d’être très attentif lors de l’utilisation des dépendances.

La bonne réponse à cette question est un fichier de configuration approprié pour les tests.J'ai eu le même problème et je l'ai résolu en créant une suite de tests avec l'ordre des fichiers de test nécessaire :

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>

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

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

À mon avis, prenons le scénario suivant dans lequel 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

un.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

un.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'));
?>

Solution alternative:Utilisez des fonctions statiques (!) 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 des tests 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 créer une hiérarchie de vos tests.
  • Vous pouvez conserver la propriété selon laquelle chaque scénario de test est totalement séparé des autres (si vous nettoyez la base de données après chaque test).
  • Et le plus important, si par exemple, la manière de se connecter change à l'avenir, vous modifiez uniquement 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 de LoginTest) :)

Lorsque j'exécute, testez mon script, nettoyez la base de données au début.Ci-dessus j'utilise mon SeleniumClearTestCase classe (je fais une capture d'écran () et d'autres fonctions intéressantes là-bas), c'est une extension de MigrationToSelenium2 (depuis github, pour porter les tests enregistrés dans Firefox en utilisant le plugin seleniumIDE + ff "Selenium IDE :PHP Formatters" ) qui est une extension de ma classe LaravelTestCase (c'est une copie de Illuminate\Foundation esting estCase mais n'étend pas PHPUnit_Framework_TestCase) qui configure Laravel pour avoir accès à éloquent lorsque nous voulons nettoyer la base de données à la fin du test) qui est extension de PHPUnit_Extensions_Selenium2TestCase.Pour configurer Laravel eloquent, j'ai également dans SeleniumClearTestCase la fonction createApplication (qui est appelée à setUp, et je prends cette fonction de laral test/TestCase)

Il y a vraiment un problème avec vos tests s'ils doivent être exécutés 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 reproductibles (et donc déboguables).

Vérifier ce site pour toute une série d'idées/informations sur la façon de factoriser vos tests de manière à éviter ce genre de problèmes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top