Executați teste PHPUnit în anumite comenzi

Există o modalitate de a obține testele din interiorul unui TestCase pentru a rula într-o anumită ordine? De exemplu, vreau să deosebesc ciclul de viață al unui obiect de creație pentru a fi folosit la distrugere, dar trebuie să mă asigur că primul obiect este setat înainte de a rula celelalte teste.

0
fr hi bn
Un caz de utilizare suplimentar care nu pare să fi fost acoperit: Poate că toate testele sunt atomice, dar unele teste sunt SLOW. Vreau ca testele rapide să fie executate cât mai repede, astfel încât acestea să poată eșua repede și orice teste lent să fie executate ultima dată, după ce am văzut deja alte probleme și pot ajunge imediat la ele.
adăugat autor Kzqai, sursa
Puteți adăuga @depends așa cum este descris într-un răspuns de mai jos, iar folosind setarea() și teardown() este, de asemenea, o idee bună, dar testele sunt doar rulate de sus în jos ...
adăugat autor Andrew, sursa

8 răspunsuri

Există într-adevăr o problemă cu testele dvs. dacă acestea trebuie să ruleze într-o anumită ordine. Fiecare test ar trebui să fie complet independent de celelalte: vă ajută cu localizarea defectelor și vă permite să obțineți rezultate repetibile (și prin urmare debugabile).

Verificați acest site pentru o întreagă încărcătură de idei/informații despre modul în care trebuie să faci testele într-un mod în care eviți aceste tipuri de probleme.

0
adăugat
PHPUnit suportă dependențele de testare prin intermediul @depends.
adăugat autor mjs, sursa

Poate că există o problemă de proiectare în testele tale.

De obicei, fiecare test nu trebuie să depindă de alte teste, astfel încât acestea să poată fi executate în orice ordine.

Fiecare test trebuie să instanțieze și să distrugă tot ce are nevoie pentru a alerga, aceasta ar fi abordarea perfectă, nu ar trebui să împărtășiți niciodată obiecte și stări între teste.

Poți să fii mai specific de ce ai nevoie de același obiect pentru testele N?

0
adăugat
Dacă constructorul este complicat faci ceva greșit, probabil că clasa ta face prea mult. Vă rugăm să citiți despre "SOLID", mai specific cu privire la "Modelul de responsabilitate unică (SRP)", de asemenea, ar trebui să "falsificați" dependențele din testele dvs. folosind machete, vă rugăm să citiți și despre "bătăi de cap, falsuri și furtuni".
adăugat autor Fabio Gomes, sursa
Ar putea exista, de asemenea, un motiv practic. De exemplu, în cazul în care curățarea trebuie să faceți, este nevoie de mai mult timp, puteți utiliza funcția tearDownAfterClass astfel încât să o executați doar o singură dată. Dacă un anumit test necesită un șablon curat, atunci fie trebuie să vă asigurați că testul se execută mai întâi, fie să apelați manual funcția tearDownAfterClass la începutul acestuia, cauzând executarea acestuia de două ori. Da, acesta este probabil un semn că ceva nu este în regulă cu designul clasei de test, dar există cazuri legitime în care testele de comandă sunt u
adăugat autor Benubird, sursa
Asta nu mi se pare corect. Punctul testului unității este de a testa o întreagă unitate. Punctul de a avea o unitate este de a grupa lucrurile împreună care trebuie să depindă una de cealaltă. Scrierea testelor care testează metode individuale fără context pentru clasă este similară cu susținerea programării procedurale peste oo deoarece susțin că funcțiile individuale nu ar trebui să depindă de aceleași date.
adăugat autor doliver, sursa
Nu sunt de acord cu punctul dvs. de vedere. Rezultatul unui test de instantare este un obiect valid care poate fi folosit de alte teste din suita de test. Nu este nevoie să instanțiați un obiect nou pentru fiecare test, în special dacă constructorul este complicat.
adăugat autor pedromanoel, sursa
Cel puțin pentru testarea bazelor de date reutilizarea obiectelor (cel puțin conexiunea) este adesea necesară. PHPUnit se referă și la acest lucru: phpunit.de/manual/current/en/database.html (consultați: Sfat: utilizați propria dvs. bază de date AbstractCaseCase)
adăugat autor emfi, sursa
@emfi dacă testați o bază de date reală, nu faceți testele unitate . Faceți teste funcționale . Pentru a efectua teste unitare, trebuie să bateți adaptorul DB și, așa cum spune Fabio, trebuie să instanțiați SUT (System Under Test) la fiecare test-run. Puteți folosi metoda setUp() protejată pentru a pregăti machetele dacă există lucruri pe care le veți repeta pentru fiecare încercare.
adăugat autor Xavi Montero, sursa

În opinia mea, luați următorul scenariu în care trebuie să testați crearea și distrugerea unei anumite resurse.

Inițial am avut două metode, A. testCreateResource și b. testDestroyResource

A. testCreateResource

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

b. testDestroyResource

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

Cred că aceasta este o idee proastă, deoarece testDestroyResource depinde de testCreateResource. Și ar fi o practică mai bună

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'));
?>
0
adăugat
-1 În cea de-a doua abordare, destroyResource depinde, de asemenea, de createResource, dar nu este explicit stabilit ca atare. În cazul în care createResource eșuează, Cadrul de testare va arata în mod eronat faptul că destroyResource nu funcționează
adăugat autor Tivie, sursa

PHPUnit suportă dependențele de testare prin @ depinde de adnotarea .

Iată un exemplu din documentația în care testele vor fi executate într-o ordine care satisface dependențele, fiecare test dependent depasind un argument la următorul:

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

Cu toate acestea, este important să rețineți că testele cu dependențe nerezolvate vor fi executate nu (de dorit, deoarece acest lucru aduce atenția rapidă la testul de eșec). Deci, este important să acorzi o atenție deosebită atunci când folosiți dependențe.

0
adăugat
Doar pentru a extinde la @Dereckson, adnotarea @depends va duce la omisie unui test dacă testul care depinde de fie nu a fost încă rulat, a fugit.
adăugat autor ogc-nick, sursa
Nu rezolvă problema cu ordinul de testare
adăugat autor Gino Pane, sursa
Pentru PHPUnit, aceasta înseamnă că funcția de testare va fi omisă dacă testul anterior nu a fost executat. Aceasta nu creează o comandă de testare.
adăugat autor Dereckson, sursa

Solutie alternativa: Utilizați funcțiile statice (!) În testele dvs. pentru a crea elemente reutilizabile. De exemplu (folosesc seleniul IDE pentru a înregistra teste și phpunit-selenium (github) pentru a rula testul în 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, '[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]"));
    }
}

Ok, și acum, pot folosi aceste elemente reutilizabile în alte test :) De exemplu:

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();
    }
  • În acest fel, puteți construi o ierarhie a testelor dvs.
  • Puteți să vă păstrați oțelul pe care fiecare caz de testare este complet separat de celălalt (dacă curățați DB după fiecare test).
  • Și cel mai important, dacă, de exemplu, modul de conectare se schimbă în viitor, modificați doar clasa LoginTest și nu aveți nevoie de o parte corectă de conectare în alte teste (ar trebui să funcționeze după actualizarea LoginTest):)

Când rulez test, scriptul meu curăță db și începutul. De mai sus folosesc clasa mea SeleniumClearTestCase (fac screenshot() și alte funcții frumoase acolo) este o extensie a MigrationToSelenium2 (de la github la testele înregistrate în firefox folosind seleniumIDE + ff plugin "Selenium IDE: PHP Formatters") care este o extensie a clasei mele LaravelTestCase (este o copie a Illuminate \ Foundation \ Testing \ TestCase dar nu extinde PHPUnit_Framework_TestCase) care configurează laravel să aibă acces la elocvent când vrem să curăim DB la sfârșitul testului), care este o extensie a PHPUnit_Extensions_Selenium2TestCase. Pentru a configura laravel elocvent, am, de asemenea, în funcția SeleniumClearTestCase createApplication (care se numește la setUp și iau această funcție din testul laral/TestCase)

0
adăugat
Iată mai multe detalii pentru a rula testul înregistrat în Selenium IDE pe Laravel 5.2 și phpUnit: stackoverflow.com/questions/33845828/…
adăugat autor Kamil Kiełczewski, sursa

Răspunsul corect pentru acest lucru este un fișier de configurare adecvat pentru teste. Am avut aceeasi problema si am fixat-o creand un test cu urmatoarea ordine:

phpunit.xml:


    
        
            file1 //this will be run before file2
            file2 //this depends on file1
        
    

0
adăugat
Cred că aceasta este singura soluție fiabilă
adăugat autor emfi, sursa

Dacă doriți ca testele dvs. să partajeze diferite obiecte și setări de ajutor, puteți utiliza setUp() , tearDown() pentru a adăuga la proprietatea sharedFixture .

0
adăugat
Puteți încă assertEquals() , etc în setUp() ? Este acea practică rea?
adăugat autor jchook, sursa

PHPUnit permite utilizarea adnotării "@depends" care specifică cazurile de testare dependente și permite trecerea argumentelor între cazuri de testare dependente.

0
adăugat
PHP România, Moldova
PHP România, Moldova
173 participanți

Vorbim despre Yii, Laravel, Symphony, MySQL, PgSQL, WP, OpenCart... Pentru confort, opriți notificările. Parteneri: https://ciupacabra.com @js_ro @node_ro @python_ro @seo_ro @Romania_Bot Offtop: @holywars_ro Joburi: @php_job @Grupuri_IT