Članki

Naučite se izvajati teste v Laravelu s preprostimi primeri z uporabo PHPUnit in PEST

Ko gre za avtomatizirane teste ali teste enot, v katerem koli programskem jeziku obstajata dve nasprotujoči si mnenji:

  • Izguba časa
  • Brez tega ne gre

Torej, s tem člankom bomo poskušali prepričati prvega, predvsem s prikazom, kako enostavno je začeti z avtomatiziranim testiranjem v Laravelu.

Najprej se pogovorimo o "zakaj", nato pa si oglejmo nekaj primerov, kako.

Zakaj potrebujemo avtomatizirano testiranje

Avtomatski testi izvajajo dele kode in poročajo o morebitnih napakah. Tako bi jih najenostavneje opisali. Predstavljajte si, da bi uvedli novo funkcijo v aplikaciji, nato pa bi osebni robotski pomočnik šel in ročno preizkusil novo funkcijo, hkrati pa preizkusil, ali nova koda ni pokvarila katere od starih funkcij.

To je glavna prednost: samodejno ponovno testiranje vseh funkcij. To se morda zdi dodatno delo, vendar če ne ukažete "robotu", naj to stori, bi morali to narediti ročno, kajne? 

Ali pa bi lahko izdali nove funkcije, ne da bi preizkusili, ali delujejo, v upanju, da bodo uporabniki poročali o napakah.

Avtomatski testi nam lahko prinesejo številne prednosti:

  • Prihranite čas ročnega testiranja;
  • Omogočajo vam, da prihranite čas tako pri novi implementirani funkciji kot pri konsolidiranih funkcijah, tako da se izognete regresiji;
  • Pomnožite to ugodnost z vsemi novimi in že implementiranimi funkcijami;
  • Prejšnje tri točke veljajo za vsako novo različico;
  • ...

Poskusite si predstavljati svojo aplikacijo čez leto ali dve, z novimi razvijalci v ekipi, ki ne poznajo kode, napisane v prejšnjih letih, ali celo, kako jo preizkusiti. 

Naši prvi avtomatizirani testi

Za izvedbo prvega avtomatizirano testiranje v Laravelu, vam ni treba napisati nobene kode. Da, prav ste prebrali. Vse je že konfigurirano in pripravljeno v prednamestitvidefinite of Laravel, vključno s prvim osnovnim primerom.

Lahko poskusite namestiti projekt Laravel in takoj zaženete prve teste:

laravel new project
cd project
php artisan test

To bi moral biti rezultat v vaši konzoli:

Če pogledamo preddefikonec Laravela /tests, imamo dve datoteki:

testi/Feature/ExampleTest.php :

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

Ni vam treba poznati sintakse, da bi razumeli, kaj se tukaj dogaja: naložite domačo stran in preverite, ali koda stanja HTTP è "200 OK".

Znano tudi kot ime metode test_the_application_returns_a_successful_response() postane berljivo besedilo, ko si ogledate rezultate testa, preprosto tako, da podčrtani simbol zamenjate s presledkom.

testi/Enota/ExampleTest.php :

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

Malo nesmiselno se zdi preverjanje, ali je to res? 

O testih enot bomo govorili malo kasneje. Zaenkrat morate razumeti, kaj se običajno zgodi v vsakem testu.

  • Vsaka testna datoteka v mapi /tests je razred PHP, ki razširja TestCase of PHPUnit
  • Znotraj vsakega razreda lahko ustvarite več metod, običajno eno metodo za situacijo, ki jo želite preizkusiti
  • Znotraj vsake metode so trije ukrepi: priprava situacije, nato ukrepanje in nato preverjanje (potrditev), ali je izid pričakovan.

Strukturno je to vse, kar morate vedeti, vse ostalo je odvisno od natančnih stvari, ki jih želite preizkusiti.

Če želite ustvariti prazen testni razred, preprosto zaženite ta ukaz:

php artisan make:test HomepageTest

Datoteka je ustvarjena 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);
    }
}

Zdaj pa poglejmo, kaj se zgodi, če testna koda ne uspe v Laravelu

Poglejmo zdaj, kaj se zgodi, če testne trditve ne vrnejo pričakovanega rezultata.

Spremenimo primer testov v tole:

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

In zdaj, če zaženemo ukaz php artisan test ponovno:

 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

Obstajata dva neuspešna preizkusa, označena kot NEUSPEŠNO, s spodnjimi razlagami in puščicami, ki kažejo na natančno linijo neuspešnih testov. Napake so prikazane na ta način.

Primer: Testiranje kode registracijskega obrazca v Laravelu

Recimo, da imamo obrazec in moramo preizkusiti različne primere: preverimo, ali ne uspe z neveljavnimi podatki, preverimo, ali uspe s pravilnim vnosom itd.

Uradni začetni komplet avtor Laravel Breeze vključuje i testiranje funkcionalnosti v njej. Poglejmo nekaj primerov od tam:

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

Tukaj imamo dva testa v enem razredu, saj sta oba povezana z obrazcem za registracijo: eden preveri, ali je obrazec pravilno naložen, drugi pa preveri, ali oddaja deluje dobro.

Spoznajmo še dve metodi za preverjanje rezultata, še dve trditvi: $this->assertAuthenticated()$response->assertRedirect(). Vse trditve lahko preverite v uradni dokumentaciji PHPUnit e Odziv Laravel . Upoštevajte, da se na to temo pojavlja nekaj splošnih trditev $this, drugi pa preverjajo konkretne $responseod klica poti.

Druga pomembna stvar je use RefreshDatabase;izjava s črto, vstavljeno nad razred. To je potrebno, kadar lahko preizkusna dejanja vplivajo na bazo podatkov, kot v tem primeru beleženje doda nov vnos v userstabela baze podatkov. Za to bi morali ustvariti ločeno preskusno bazo podatkov, ki bo posodobljena z php artisan migrate:freshvsakič, ko se izvajajo testi.

Imate dve možnosti: fizično ustvarite ločeno bazo podatkov ali uporabite bazo podatkov SQLite v pomnilniku. Oba sta konfigurirana v datoteki phpunit.xmlprivzetodefinita z Laravel. Natančneje, potrebujete ta del:

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

Glej DB_CONNECTIONDB_DATABASEkatere so komentirane? Če imate na svojem strežniku SQLite, je najenostavnejše dejanje, da preprosto odkomentirate te vrstice in vaši testi se bodo izvajali proti tej bazi podatkov v pomnilniku.

Pri tem testu pravimo, da je uporabnik uspešno overjen in preusmerjen na pravilno domačo stran, lahko pa tudi preizkusimo dejanske podatke v bazi podatkov.

Poleg te kode:

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

Lahko tudi uporabimo preskusne trditve baze podatkov in naredi nekaj takega:

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

Primer prijavne strani

Oglejmo si zdaj še en primer prijavne strani z 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();
    }
}

Gre za obrazec za prijavo. Logika je podobna registraciji, kajne? Toda tri metode namesto dveh, zato je to primer testiranja dobrih in slabih scenarijev. Torej je skupna logika, da morate preizkusiti oba primera: ko gredo stvari dobro in ko ne uspejo.

Glasilo o inovacijah
Ne zamudite najpomembnejših novic o inovacijah. Prijavite se, če jih želite prejemati po e-pošti.

Kar vidite v tem testu, je tudi uporaba Tovarne baz podatkov : Laravel ustvari lažnega uporabnika ( ponovno v vaši posodobljeni preskusni bazi podatkov ) in se nato poskuša prijaviti s pravilnimi ali nepravilnimi poverilnicami.

Še enkrat Laravel ustvari tovarniško predefinita z lažnimi podatki za Usermodel, zunaj škatle.

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

Vidite, koliko stvari je pripravil Laravel sam, ali bi nam bilo enostavno začeti s testiranjem?

Če torej izvršimo php artisan testpo namestitvi Laravel Breeze bi morali videti nekaj takega:

 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

Funkcionalni testi v primerjavi s testi enot in drugimi

Videli ste podmape tests/Feature e tests/Unit ?. 

Kakšna je razlika med njimi? 

Globalno, zunaj ekosistema Laravel/PHP, obstaja več vrst avtomatiziranega testiranja. Najdete lahko izraze, kot so:

  • Preizkusi enot
  • Testiranje funkcij
  • Integracijski testi
  • Funkcionalni testi
  • Testiranje od konca do konca
  • Sprejemni testi
  • Preskusi dima
  • itd.

Sliši se zapleteno in dejanske razlike med temi vrstami testov so včasih zabrisane. Zato je Laravel poenostavil vse te zmedene izraze in jih združil v dva: enota/funkcija.

Preprosto povedano, preizkusi funkcij poskušajo izvesti dejansko funkcionalnost vaših aplikacij: pridobite URL, pokličite API, posnemajte natančno vedenje, kot je izpolnjevanje obrazca. Preizkusi funkcij običajno izvajajo enake ali podobne operacije, kot bi jih ročno v resničnem življenju naredil kateri koli uporabnik projekta.

Preizkusi enot imajo dva pomena. Na splošno boste morda ugotovili, da se kateri koli avtomatizirani test imenuje »testiranje enot«, celoten postopek pa lahko imenujemo »testiranje enot«. Toda v kontekstu funkcionalnosti v primerjavi z enoto gre pri tem procesu za testiranje specifične nejavne enote kode v izolaciji. Na primer, imate razred Laravel z metodo, ki nekaj izračuna, na primer skupno ceno naročila s parametri. Zato bi test enote pokazal, ali ta metoda (enota kode) vrne pravilne rezultate z različnimi parametri.

Če želite ustvariti test enote, morate dodati zastavico:

php artisan make:test OrderPriceTest --unit

Ustvarjena koda je enaka testu pred enotodefiLaravel sistem:

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

Kot vidite, ne obstaja RefreshDatabase, in to je eden od definajpogostejše definicije testa enote: ne dotika se baze podatkov, deluje kot »črna skrinjica«, izolirana od delujoče aplikacije.

Če poskušamo posnemati primer, ki sem ga prej omenil, si predstavljajmo, da imamo servisni razred OrderPrice.

app/Services/OrderPriceService.php:

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

Nato bi lahko test enote izgledal nekako takole:

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
}

Po mojih osebnih izkušnjah s projekti Laravel je velika večina testov testov funkcij, ne testov enot. Najprej morate preizkusiti, ali vaša aplikacija deluje tako, kot bi jo uporabljali pravi ljudje.

Naprej, če imate posebne izračune ali logiko, lahko definire kot enota, s parametri, lahko ustvarite teste enote posebej za to.

Včasih pisanje testov zahteva spreminjanje same kode in njeno refaktoriranje, da postane bolj »testno«: ločevanje enot v posebne razrede ali metode.

Kdaj/kako opraviti teste?

Kakšna je dejanska uporaba tega php artisan test, kdaj naj ga zaženeš?

Obstajajo različni pristopi, odvisno od vašega poslovnega poteka dela, vendar morate na splošno zagotoviti, da so vsi testi »zeleni« (tj. brez napak), preden potisnete končne spremembe kode v repozitorij.

Nato delate lokalno na svoji nalogi in ko mislite, da ste končali, zaženite nekaj testov, da se prepričate, da niste ničesar pokvarili. Ne pozabite, da lahko vaša koda povzroči napake ne samo v vaši lastni logiki, ampak tudi nenamerno prekine neko drugo vedenje v kodi nekoga drugega, ki je bila napisana že zdavnaj.

Če naredimo še korak naprej, je možna avtomatizacija veliko stvari. Z različnimi orodji CI/CD lahko določite preizkuse, ki se izvajajo vsakič, ko nekdo potisne spremembe v določeno vejo Git ali pred združitvijo kode v produkcijsko vejo. Najenostavnejši potek dela bi bil uporaba Github Actions, kar imam ločen video kar dokazuje.

Kaj morate testirati?

Obstajajo različna mnenja o tem, kako velika naj bi bila tako imenovana "testna pokritost": poskusite vsako možno operacijo in primer na vsaki strani ali omejite delo na najpomembnejše dele.

Pravzaprav se tukaj strinjam z ljudmi, ki avtomatizirano testiranje obtožujejo, da vzame več časa, kot da prinese dejansko korist. To se lahko zgodi, če pišete teste za vsako podrobnost. Kljub temu to morda zahteva vaš projekt: glavno vprašanje je "kakšna je cena morebitne napake".

Z drugimi besedami, določiti morate prednost pri testiranju tako, da postavite vprašanje "Kaj bi se zgodilo, če ta koda ne uspe?" Če ima vaš plačilni sistem napake, bo to neposredno vplivalo na poslovanje. Torej, če je funkcionalnost vaših vlog/dovoljenj pokvarjena, je to velika varnostna težava.

Všeč mi je, kako je Matt Stauffer povedal na konferenci: "Najprej morate preizkusiti tiste stvari, zaradi katerih bi vas, če ne uspejo, odpustili iz službe." Seveda je to pretiravanje, vendar razumete: najprej poskusite pomembne stvari. In potem druge funkcije, če imate čas.

PEST: nova alternativa PHPUnitu

Vsi zgornji primeri temeljijo na orodju za predhodno testiranje Laraveldefinoč: PHPUnit . Toda z leti so se v ekosistemu pojavila druga orodja in eno najnovejših priljubljenih je ŠKODLJIVCA . Ustvaril uradni uslužbenec Laravela Nuno Maduro , želi poenostaviti sintakso, zaradi česar je pisanje kode za teste še hitrejše.

Pod pokrovom motorja teče su PHPUnit, kot dodatna plast, samo poskuša minimizirati nekatere vnaprej ponovljene deledefikonca kode PHPUnit.

Poglejmo si primer. Ne pozabite na razred predhodnega preizkusa funkcijdefini v Laravelu? Spomnil vas bom:

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

Ali veste, kako bi izgledal isti test s PEST?

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

Da, ENA vrstica kode in to je to. Torej, cilj PEST je odstraniti režijske stroške:

  • Ustvarjanje razredov in metod za vse;
  • Razširitev testnega primera;
  • S postavitvijo dejanj v ločene vrstice: v PEST jih lahko povežete.

Če želite ustvariti test PEST v Laravelu, morate določiti dodatno zastavico:

php artisan make:test HomepageTest --pest

Od tega pisanja je PEST precej priljubljen med razvijalci Laravel, vendar je vaša osebna želja, ali boste uporabili to dodatno orodje in se naučili njegove sintakse ter opombe PHPUnit.

BlogInnovazione.it

Glasilo o inovacijah
Ne zamudite najpomembnejših novic o inovacijah. Prijavite se, če jih želite prejemati po e-pošti.

Nedavni članki

Založniki in OpenAI podpisujejo sporazume za urejanje pretoka informacij, ki jih obdeluje umetna inteligenca

Prejšnji ponedeljek je Financial Times objavil dogovor z OpenAI. FT licencira svoje vrhunsko novinarstvo ...

April 30 2024

Spletna plačila: Evo, kako vam storitve pretakanja omogočajo večno plačevanje

Milijoni ljudi plačujejo storitve pretakanja in plačujejo mesečne naročnine. Splošno mnenje je, da si…

April 29 2024

Veeam ponuja najobsežnejšo podporo za izsiljevalsko programsko opremo, od zaščite do odziva in obnovitve

Coveware by Veeam bo še naprej zagotavljal storitve odzivanja na incidente kibernetskega izsiljevanja. Coveware bo nudil forenziko in zmogljivosti sanacije ...

April 23 2024

Zelena in digitalna revolucija: kako predvideno vzdrževanje preoblikuje naftno in plinsko industrijo

Prediktivno vzdrževanje revolucionira sektor nafte in plina z inovativnim in proaktivnim pristopom k upravljanju obratov.…

April 22 2024