Domanda

C'è un modo per ottenere i test all'interno di a TestCase eseguire in un certo ordine?Ad esempio, voglio separare il ciclo di vita di un oggetto dalla creazione all'utilizzo fino alla distruzione, ma devo assicurarmi che l'oggetto sia configurato prima di eseguire gli altri test.

È stato utile?

Soluzione

Forse c'è un problema di progettazione nei tuoi test.

Di solito ogni test non deve dipendere da altri test, quindi possono essere eseguiti in qualsiasi ordine.

Ogni test deve istanziare e distruggere tutto ciò di cui ha bisogno per essere eseguito, questo sarebbe l'approccio perfetto, non dovresti mai condividere oggetti e stati tra i test.

Puoi essere più specifico sul motivo per cui hai bisogno dello stesso oggetto per i test N?

Altri suggerimenti

PHPUnit supporta le dipendenze di test tramite il file @dipende annotazione.

Ecco un esempio tratto dalla documentazione in cui i test verranno eseguiti in un ordine che soddisfi le dipendenze, con ciascun test dipendente che passa un argomento al successivo:

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

Tuttavia, è importante notare che i test con dipendenze non risolte lo faranno non essere eseguito (auspicabile, poiché ciò porta rapidamente l'attenzione sul test fallito).Pertanto, è importante prestare molta attenzione quando si utilizzano le dipendenze.

La risposta corretta a questa domanda è un file di configurazione adeguato per i test.Ho avuto lo stesso problema e l'ho risolto creando testsuite con l'ordine dei file di test necessario:

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>

Se vuoi che i tuoi test condividano vari oggetti e impostazioni di supporto, puoi utilizzare setUp(), tearDown() da aggiungere al sharedFixture proprietà.

PHPUnit consente l'uso dell'annotazione '@depends' che specifica casi di test dipendenti e consente il passaggio di argomenti tra casi di test dipendenti.

A mio avviso, prendi il seguente scenario in cui devo testare la creazione e la distruzione di una particolare risorsa.

Inizialmente avevo due metodi, a.testCreateResource e b.testDestroyResource

UN.testCreateResource

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

B.testDestroyResource

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

Penso che questa sia una cattiva idea, poiché testDestroyResource dipende da testCreateResource.E una pratica migliore sarebbe quella di farlo

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

Soluzione alternativa:Utilizza funzioni statiche(!) nei tuoi test per creare elementi riutilizzabili.Ad esempio (utilizzo l'IDE selenium per registrare test e phpunit-selenium (github) per eseguire test all'interno del browser)

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, e ora posso usare questi elementi riutilizzabili in altri test :) Ad esempio:

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();
    }
  • In questo modo, puoi creare una gerarchia dei tuoi test.
  • Puoi mantenere la proprietà che ogni caso di test è totalmente separato dagli altri (se pulisci DB dopo ogni test).
  • E, cosa più importante, se, ad esempio, il modo di accedere cambia in futuro, modifichi solo la classe LoginTest e non hai bisogno della parte di accesso corretta in altri test (dovrebbero funzionare dopo l'aggiornamento LoginTest) :)

Quando eseguo test, il mio script pulisce db dall'inizio.Sopra uso il mio SeleniumClearTestCase class (creo screenshot() e altre funzioni carine lì) è l'estensione di MigrationToSelenium2 (da github, per portare i test registrati in Firefox utilizzando il plugin seleniumIDE + ff "Selenium IDE:PHP Formatters") che è l'estensione della mia classe LaravelTestCase (è una copia di Illuminate\Foundation esting estCase ma non estende PHPUnit_Framework_TestCase) che configura laravel per avere accesso a eloquent quando vogliamo pulire il DB alla fine del test) che è estensione di PHPUnit_Extensions_Selenium2TestCase.Per impostare laravel eloquent ho anche nella funzione SeleniumClearTestCase createApplication (che viene chiamata in setUp, e prendo questa funzione da laral test/TestCase)

C'è davvero un problema con i test se devono essere eseguiti in un determinato ordine.Ogni test dovrebbe essere totalmente indipendente dagli altri:ti aiuta con la localizzazione dei difetti e ti consente di ottenere risultati ripetibili (e quindi debuggabili).

Guardare questo sito per un sacco di idee / informazioni, su come fattorizzare i tuoi test in modo da evitare questo tipo di problemi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top