tovar

Naučte sa robiť testy v Laravel s jednoduchými príkladmi pomocou PHPUnit a PEST

Pokiaľ ide o automatizované testy alebo jednotkové testy, v akomkoľvek programovacom jazyku existujú dva protichodné názory:

  • Stráta času
  • Bez toho sa nezaobídete

Takže týmto článkom sa pokúsime presvedčiť tých prvých, najmä tým, že ukážeme, aké ľahké je začať s automatizovaným testovaním v Laravel.

Najprv si povedzme o „prečo“ a potom si pozrime niekoľko príkladov ako.

Prečo potrebujeme automatizované testovanie

Automatizované testy spúšťajú časti kódu a hlásia všetky chyby. To je najjednoduchší spôsob, ako ich opísať. Predstavte si, že v aplikácii spustíte novú funkciu a potom by šiel osobný robotický asistent manuálne otestovať novú funkciu a zároveň by testoval, či nový kód neporušil niektorú zo starých funkcií.

Toto je hlavná výhoda: automatické opätovné testovanie všetkých funkcií. Môže sa to zdať ako práca navyše, ale ak nepoviete „robotovi“, aby to urobil, mali by sme to urobiť manuálne, však? 

Alebo nové funkcie môžu byť uvoľnené bez testovania, či fungujú, dúfajúc, že ​​používatelia budú hlásiť chyby.

Automatizované testy nám môžu poskytnúť niekoľko výhod:

  • Ušetrite čas manuálneho testovania;
  • Umožňujú vám ušetriť čas na implementovanej novej funkcii aj na konsolidovaných funkciách tým, že sa vyhnú regresii;
  • Vynásobte túto výhodu všetkými novými funkciami a všetkými už implementovanými funkciami;
  • Predchádzajúce tri body platia pre každú novú verziu;
  • ...

Skúste si predstaviť svoju aplikáciu o rok alebo dva, s novými vývojármi v tíme, ktorí nepoznajú kód napísaný v predchádzajúcich rokoch, ani nepoznajú, ako ho otestovať. 

Naše prvé automatizované testy

Na vykonanie prvého automatizované testovanie v Laravel, nemusíte písať žiadny kód. Áno, čítate správne. Všetko je už nakonfigurované a pripravené v predinštaláciidefinite of Laravel, vrátane úplne prvého základného príkladu.

Môžete skúsiť nainštalovať projekt Laravel a okamžite spustiť prvé testy:

laravel new project
cd project
php artisan test

Toto by mal byť výsledok vo vašej konzole:

Ak sa pozrieme na preddefimiesto Laravelu /tests, máme dva súbory:

testy/Feature/ExampleTest.php :

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/');
 
        $response->assertStatus(200);
    }
}

Nepotrebujete poznať žiadnu syntax, aby ste pochopili, čo sa tu deje: načítajte domovskú stránku a skontrolujte, či je stavový kód HTTP è "200 OK".

Tiež známy ako názov metódy test_the_application_returns_a_successful_response() sa stane čitateľným textom, keď si prezeráte výsledky testu, jednoducho nahradením symbolu podčiarknutia medzerou.

testy/Unit/ExampleTest.php :

class ExampleTest extends TestCase
{
    public function test_that_true_is_true()
    {
        $this->assertTrue(true);
    }
}

Zdá sa to trochu zbytočné, overiť si, či je to pravda? 

Konkrétne o jednotkových testoch si povieme o niečo neskôr. Zatiaľ musíte pochopiť, čo sa vo všeobecnosti deje v každom teste.

  • Každý testovací súbor v priečinku /tests je trieda PHP, ktorá rozširuje TestCase of PHPUnit
  • V rámci každej triedy môžete vytvoriť viacero metód, zvyčajne jednu metódu na testovanie situácie
  • V rámci každej metódy existujú tri akcie: príprava situácie, potom akcia a potom overenie (potvrdenie), či je výsledok taký, ako sa očakávalo.

Štrukturálne, to je všetko, čo potrebujete vedieť, všetko ostatné závisí od presných vecí, ktoré chcete testovať.

Ak chcete vygenerovať prázdnu testovaciu triedu, jednoducho spustite tento príkaz:

php artisan make:test HomepageTest

Súbor sa vygeneruje tests/Feature/HomepageTest.php:

class HomepageTest extends TestCase
{
    // Replace this method with your own ones
    public function test_example()
    {
        $response = $this->get('/');
 
        $response->assertStatus(200);
    }
}

Teraz sa pozrime, čo sa stane, ak testovací kód zlyhá v Laravel

Pozrime sa teraz, čo sa stane, ak testovacie tvrdenia nevrátia očakávaný výsledok.

Zmeňme príklady testov na toto:

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/non-existing-url');
 
        $response->assertStatus(200);
    }
}
 
 
class ExampleTest extends TestCase
{
    public function test_that_true_is_false()
    {
        $this->assertTrue(false);
    }
}

A teraz, ak spustíme príkaz php artisan test opäť:

 FAIL  Tests\Unit\ExampleTest
⨯ that true is true
 
 FAIL  Tests\Feature\ExampleTest
⨯ the application returns a successful response
 
---
 
• Tests\Unit\ExampleTest > that true is true
Failed asserting that false is true.
 
at tests/Unit/ExampleTest.php:16
   12▕      * @return void
   13▕      */
   14▕     public function test_that_true_is_true()
   15▕     {
➜  16▕         $this->assertTrue(false);
   17▕     }
   18▕ }
   19▕
 
• Tests\Feature\ExampleTest > the application returns a successful response
Expected response status code [200] but received 404.
Failed asserting that 200 is identical to 404.
 
at tests/Feature/ExampleTest.php:19
   15▕     public function test_the_application_returns_a_successful_response()
   16▕     {
   17▕         $response = $this->get('/non-existing-url');
   18▕
➜  19▕         $response->assertStatus(200);
   20▕     }
   21▕ }
   22▕
 
 
Tests:  2 failed
Time:   0.11s

Existujú dva neúspešné testy, označené ako FAIL, s vysvetleniami nižšie a šípkami ukazujúcimi na presný riadok testov, ktoré zlyhali. Chyby sú označené týmto spôsobom.

Príklad: Testovanie kódu registračného formulára v Laravel

Predpokladajme, že máme formulár a potrebujeme otestovať rôzne prípady: skontrolujeme, či zlyhá s neplatnými údajmi, skontrolujeme, či uspeje so správnym zadaním atď.

Oficiálna štartovacia súprava od Laravel Breeze zahŕňa i testovanie funkčnosti v ňom. Pozrime sa odtiaľ na niekoľko príkladov:

tests/Feature/RegistrationTest.php

use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
 
class RegistrationTest extends TestCase
{
    use RefreshDatabase;
 
    public function test_registration_screen_can_be_rendered()
    {
        $response = $this->get('/register');
 
        $response->assertStatus(200);
    }
 
    public function test_new_users_can_register()
    {
        $response = $this->post('/register', [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password',
            'password_confirmation' => 'password',
        ]);
 
        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }
}

Tu máme dva testy v jednej triede, pretože oba súvisia s registračným formulárom: jeden kontroluje, či je formulár načítaný správne a druhý kontroluje, či odoslanie funguje správne.

Zoznámime sa s ďalšími dvoma metódami overenia výsledku, ďalšími dvoma tvrdeniami: $this->assertAuthenticated()$response->assertRedirect(). Môžete skontrolovať všetky dostupné tvrdenia v oficiálnej dokumentácii PHPUnit e Laravelova odpoveď . Všimnite si, že na túto tému sa vyskytujú niektoré všeobecné tvrdenia $this, zatiaľ čo iní kontrolujú konkrétne $responsez hovoru na trase.

Ďalšou dôležitou vecou je use RefreshDatabase;vyhlásenie s ťahom, vloženým nad triedu. Je to potrebné, keď testovacie akcie môžu ovplyvniť databázu, ako v tomto príklade, protokolovanie pridá novú položku do usersdatabázová tabuľka. Na tento účel by ste mali vytvoriť samostatnú testovaciu databázu, ktorá sa bude aktualizovať php artisan migrate:freshpri každom spustení testov.

Máte dve možnosti: fyzicky vytvoriť samostatnú databázu alebo použiť in-memory databázu SQLite. Obe sú nakonfigurované v súbore phpunit.xmlposkytované štandardnedefinita s laravel. Konkrétne potrebujete túto časť:

<php>
    <env name="APP_ENV" value="testing"/>
    <env name="BCRYPT_ROUNDS" value="4"/>
    <env name="CACHE_DRIVER" value="array"/>
    <!-- <env name="DB_CONNECTION" value="sqlite"/> -->
    <!-- <env name="DB_DATABASE" value=":memory:"/> -->
    <env name="MAIL_MAILER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="TELESCOPE_ENABLED" value="false"/>
</php>

Pozrite si DB_CONNECTIONDB_DATABASEktoré sú komentované? Ak máte na svojom serveri SQLite, najjednoduchšou akciou je jednoducho odkomentovať tieto riadky a vaše testy sa spustia proti tejto databáze v pamäti.

V tomto teste hovoríme, že používateľ je úspešne autentizovaný a presmerovaný na správnu domovskú stránku, ale môžeme otestovať aj skutočné údaje v databáze.

Okrem tohto kódu:

$this->assertAuthenticated();
$response->assertRedirect(RouteServiceProvider::HOME);

Môžeme použiť aj testovacie tvrdenia databázy a urob niečo takéto:

$this->assertDatabaseCount('users', 1);
 
// Or...
$this->assertDatabaseHas('users', [
    'email' => 'test@example.com',
]);

Príklad prihlasovacej stránky

Pozrime sa teraz na ďalší príklad prihlasovacej stránky s Laravel Breeze

tests/Feature/AuthenticationTest.php:

class AuthenticationTest extends TestCase
{
    use RefreshDatabase;
 
    public function test_login_screen_can_be_rendered()
    {
        $response = $this->get('/login');
 
        $response->assertStatus(200);
    }
 
    public function test_users_can_authenticate_using_the_login_screen()
    {
        $user = User::factory()->create();
 
        $response = $this->post('/login', [
            'email' => $user->email,
            'password' => 'password',
        ]);
 
        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }
 
    public function test_users_can_not_authenticate_with_invalid_password()
    {
        $user = User::factory()->create();
 
        $this->post('/login', [
            'email' => $user->email,
            'password' => 'wrong-password',
        ]);
 
        $this->assertGuest();
    }
}

Ide o prihlasovací formulár. Logika je podobná registrácii, však? Ale tri metódy namiesto dvoch, takže toto je príklad testovania dobrých aj zlých scenárov. Spoločnou logikou teda je, že by ste mali otestovať oba prípady: keď veci idú dobre, aj keď zlyhajú.

Inovačný bulletin
Nenechajte si ujsť najdôležitejšie novinky o inováciách. Prihláste sa na ich odber e-mailom.

Tiež to, čo vidíte v tomto teste, je použitie Databázové továrne : Laravel vytvára falošného používateľa ( opäť vo vašej aktualizovanej testovacej databáze ) a potom sa pokúsi prihlásiť so správnymi alebo nesprávnymi prihlasovacími údajmi.

Laravel opäť generuje továrenské preddefinita s nepravdivými údajmi pre Usermodel, mimo krabice.

database/factories/UserFactory.php:

class UserFactory extends Factory
{
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

Vidíte, koľko vecí pripravuje samotný Laravel, takže by bolo pre nás ľahké začať testovať?

Ak teda vykonáme php artisan testpo inštalácii Laravel Breeze by sme mali vidieť niečo takéto:

 PASS  Tests\Unit\ExampleTest
✓ that true is true
 
 PASS  Tests\Feature\Auth\AuthenticationTest
✓ login screen can be rendered
✓ users can authenticate using the login screen
✓ users can not authenticate with invalid password
 
 PASS  Tests\Feature\Auth\EmailVerificationTest
✓ email verification screen can be rendered
✓ email can be verified
✓ email is not verified with invalid hash
 
 PASS  Tests\Feature\Auth\PasswordConfirmationTest
✓ confirm password screen can be rendered
✓ password can be confirmed
✓ password is not confirmed with invalid password
 
 PASS  Tests\Feature\Auth\PasswordResetTest
✓ reset password link screen can be rendered
✓ reset password link can be requested
✓ reset password screen can be rendered
✓ password can be reset with valid token
 
 PASS  Tests\Feature\Auth\RegistrationTest
✓ registration screen can be rendered
✓ new users can register
 
 PASS  Tests\Feature\ExampleTest
✓ the application returns a successful response
 
Tests:  17 passed
Time:   0.61s

Funkčné testy v porovnaní s jednotkovými testami a inými

Videli ste podpriečinky tests/Feature e tests/Unit ?. 

Aký je medzi nimi rozdiel? 

Globálne, mimo ekosystému Laravel/PHP, existuje niekoľko typov automatizovaného testovania. Môžete nájsť výrazy ako:

  • Jednotkové testy
  • Testovanie funkcií
  • Integračné testy
  • Funkčné testy
  • End-to-end testovanie
  • Akceptačné testy
  • Dymové testy
  • atď.

Znie to komplikovane a skutočné rozdiely medzi týmito typmi testov sú niekedy rozmazané. Laravel preto všetky tieto mätúce pojmy zjednodušil a zoskupil do dvoch: jednotka/vlastnosť.

Jednoducho povedané, testy funkcií sa pokúšajú spustiť skutočnú funkčnosť vašich aplikácií: získať adresu URL, zavolať rozhranie API, napodobniť presné správanie, ako je vyplnenie formulára. Testy funkcií zvyčajne vykonávajú rovnaké alebo podobné operácie, aké by robil každý používateľ projektu manuálne v reálnom živote.

Jednotkové testy majú dva významy. Vo všeobecnosti môžete zistiť, že každý automatizovaný test sa nazýva „testovanie jednotiek“ a celý proces sa môže nazývať „testovanie jednotiek“. Ale v kontexte funkčnosti verzus jednotka je tento proces o testovaní špecifickej neverejnej jednotky kódu v izolácii. Napríklad máte triedu Laravel s metódou, ktorá niečo vypočíta, napríklad celkovú cenu objednávky s parametrami. Preto by test jednotky uviedol, či sa z tejto metódy (jednotka kódu) vrátia správne výsledky s rôznymi parametrami.

Ak chcete vygenerovať test jednotky, musíte pridať príznak:

php artisan make:test OrderPriceTest --unit

Vygenerovaný kód je rovnaký ako pri teste pred jednotkoudefiLaravel systém:

class OrderPriceTest extends TestCase
{
    public function test_example()
    {
        $this->assertTrue(true);
    }
}

Ako vidíte, neexistuje RefreshDatabase, a toto je jeden z definajbežnejšie definície unit testov: nedotýka sa databázy, funguje ako „čierna skrinka“, izolovaná od spustenej aplikácie.

Snažíme sa napodobniť príklad, ktorý som už spomenul, predstavme si, že máme triedu služieb OrderPrice.

app/Services/OrderPriceService.php:

class OrderPriceService
{
    public function calculatePrice($productId, $quantity, $tax = 0.0)
    {
        // Some kind of calculation logic
    }
}

Potom by test jednotky mohol vyzerať takto:

class OrderPriceTest extends TestCase
{
    public function test_single_product_no_taxes()
    {
        $product = Product::factory()->create(); // generate a fake product
        $price = (new OrderPriceService())->calculatePrice($product->id, 1);
        $this->assertEquals(1, $price);
    }
 
    public function test_single_product_with_taxes()
    {
        $price = (new OrderPriceService())->calculatePrice($product->id, 1, 20);
        $this->assertEquals(1.2, $price);
    }
 
    // More cases with more parameters
}

Podľa mojich osobných skúseností s projektmi Laravel je veľká väčšina testov testami funkcií, nie testov jednotiek. Najprv musíte otestovať, či vaša aplikácia funguje, ako by ju používali skutoční ľudia.

Ďalej, ak máte špeciálne výpočty alebo logiku, môžete definire ako jednotku, s parametrami, môžete vytvoriť testy jednotiek špeciálne pre tento účel.

Niekedy si písanie testov vyžaduje úpravu samotného kódu a jeho prerobenie, aby bol „testovateľnejší“: rozdelenie jednotiek do špeciálnych tried alebo metód.

Kedy/ako vykonať testy?

Aké je skutočné využitie tohto php artisan test, kedy by ste to mali spustiť?

Existujú rôzne prístupy v závislosti od vášho obchodného pracovného toku, ale vo všeobecnosti sa musíte uistiť, že všetky testy sú „zelené“ (t. j. bezchybné) pred odoslaním najnovších zmien kódu do úložiska.

Potom na svojej úlohe pracujete lokálne a keď si myslíte, že ste hotový, spustite niekoľko testov, aby ste sa uistili, že ste nič neporušili. Pamätajte, že váš kód môže spôsobiť chyby nielen vo vašej vlastnej logike, ale aj neúmyselne narušiť niektoré iné správanie v kóde niekoho iného napísaného už dávno.

Ak to urobíme o krok ďalej, je možné automatizovať veľa veci. Pomocou rôznych nástrojov CI/CD môžete špecifikovať testy, ktoré sa majú spustiť vždy, keď niekto vloží zmeny do konkrétnej vetvy Git alebo pred zlúčením kódu do produkčnej vetvy. Najjednoduchším pracovným postupom by bolo použiť Github Actions, mám samostatné video čo to dokazuje.

Čo by ste mali otestovať?

Existujú rôzne názory na to, aké veľké by malo byť takzvané „testovacie pokrytie“: vyskúšajte každú možnú operáciu a prípad na každej stránke alebo obmedzte prácu na najdôležitejšie časti.

V skutočnosti tu súhlasím s ľuďmi, ktorí obviňujú automatické testovanie z toho, že zaberá viac času, než poskytuje skutočný prínos. To sa môže stať, ak píšete testy pre každý jeden detail. To znamená, že to môže vyžadovať váš projekt: hlavnou otázkou je „aká je cena potenciálnej chyby“.

Inými slovami, musíte uprednostniť svoje testovacie úsilie položením otázky „Čo by sa stalo, keby tento kód zlyhal?“ Ak má váš platobný systém chyby, bude to mať priamy vplyv na podnikanie. Ak je teda funkčnosť vašich rolí/oprávnení nefunkčná, ide o veľký bezpečnostný problém.

Páči sa mi, ako to povedal Matt Stauffer na konferencii: „Najskôr musíte otestovať veci, ktoré, ak zlyhajú, vás vyhodia z práce.“ Samozrejme, že je to prehnané, ale máte myšlienku: najskôr vyskúšajte dôležité veci. A potom ďalšie funkcie, ak máte čas.

PEST: nová alternatíva k PHPUnit

Všetky vyššie uvedené príklady sú založené na predbežnom testovacom nástroji Laraveldefinoc: PHPUnit . V priebehu rokov sa však v ekosystéme objavili ďalšie nástroje a jedným z najnovších populárnych je PEST . Vytvoril oficiálny zamestnanec Laravel Nuno Maduro , má za cieľ zjednodušiť syntax, vďaka čomu je písanie kódu pre testy ešte rýchlejšie.

Pod kapotou to beží su PHPUnit, ako ďalšia vrstva, sa len snaží minimalizovať niektoré predopakované častidefičasť kódu PHPUnit.

Pozrime sa na príklad. Pamätajte na triedu predbežného testovania funkciídefinited v Laravel? Pripomeniem ti:

namespace Tests\Feature;
 
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
 
class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/');
 
        $response->assertStatus(200);
    }
}

Viete, ako by vyzeral rovnaký test s PEST?

test('the application returns a successful response')->get('/')->assertStatus(200);

Áno, JEDEN riadok kódu a to je všetko. Takže cieľom PEST je odstrániť režijné náklady:

  • Vytváranie tried a metód pre všetko;
  • Rozšírenie testovacieho prípadu;
  • Umiestnením akcií do samostatných riadkov: v PEST ich môžete spojiť.

Ak chcete vygenerovať test PEST v Laravel, musíte zadať ďalší príznak:

php artisan make:test HomepageTest --pest

V čase písania tohto článku je PEST medzi vývojármi Laravel pomerne populárny, ale je to vaša osobná preferencia, či použijete tento dodatočný nástroj a naučíte sa jeho syntax, ako aj poznámku PHPUnit.

BlogInnovazione.it

Inovačný bulletin
Nenechajte si ujsť najdôležitejšie novinky o inováciách. Prihláste sa na ich odber e-mailom.

Nedávne články

Výhody farebných stránok pre deti - svet mágie pre všetky vekové kategórie

Rozvíjanie jemnej motoriky pomocou vyfarbovania pripravuje deti na zložitejšie zručnosti, ako je písanie. Na farbenie…

2 mája 2024

Budúcnosť je tu: Ako námorný priemysel prináša revolúciu do globálnej ekonomiky

Námorný sektor je skutočnou globálnou ekonomickou veľmocou, ktorá smerovala k 150 miliardovému trhu...

1 mája 2024

Vydavatelia a OpenAI podpisujú dohody o regulácii toku informácií spracovávaných umelou inteligenciou

Minulý pondelok Financial Times oznámili dohodu s OpenAI. FT licencuje svoju žurnalistiku svetovej triedy…

Apríla 30 2024

Online platby: Takto budete vďaka streamovacím službám platiť navždy

Milióny ľudí platia za streamovacie služby a platia mesačné predplatné. Je bežný názor, že si…

Apríla 29 2024