Straipsniai

Sužinokite, kaip atlikti Laravel testus naudodami paprastus pavyzdžius, naudodami PHPUnit ir PEST

Kalbant apie automatinius testus ar vienetų testus, bet kuria programavimo kalba yra dvi priešingos nuomonės:

  • Laiko švaistymas
  • Be jo neapsieisi

Taigi, šiuo straipsniu bandysime įtikinti pirmąjį, ypač parodydami, kaip lengva pradėti Laravel automatizuotą testavimą.

Pirmiausia pakalbėkime apie „kodėl“, o tada pažiūrėkime, kaip tai padaryti.

Kodėl mums reikia automatinio testavimo

Automatiniai testai paleidžia dalis kodo ir praneša apie visas klaidas. Tai paprasčiausias būdas juos apibūdinti. Įsivaizduokite, kad programėlėje paleidžiate naują funkciją, o tada asmeninis roboto asistentas nueina ir rankiniu būdu išbando naująją funkciją, kartu patikrindamas, ar naujasis kodas nepažeidžia senų funkcijų.

Tai yra pagrindinis pranašumas: visų funkcijų pakartotinis patikrinimas automatiškai. Tai gali atrodyti kaip papildomas darbas, bet jei neliepiate „robotui“ to padaryti, mes turėtume tai padaryti rankiniu būdu, tiesa? 

Arba naujos funkcijos gali būti išleistos neišbandžius, ar jos veikia, tikintis, kad vartotojai praneš apie klaidas.

Automatiniai testai gali suteikti mums keletą privalumų:

  • Sutaupykite rankinio testavimo laiką;
  • Jie leidžia sutaupyti laiko tiek naujai įdiegtai, tiek konsoliduotoms funkcijoms, išvengiant regresijos;
  • Padauginkite šią naudą iš visų naujų ir visų jau įdiegtų funkcijų;
  • Ankstesni trys punktai taikomi kiekvienai naujai versijai;
  • ...

Pabandykite įsivaizduoti savo programą po metų ar dvejų su naujais kūrėjais komandoje, kurie nežino ankstesniais metais parašyto kodo ar net nežino, kaip jį išbandyti. 

Mūsų pirmieji automatiniai testai

Norėdami atlikti pirmąjį automatinis testavimas Laravel, jums nereikia rašyti jokio kodo. Taip, jūs perskaitėte teisingai. Viskas jau sukonfigūruota ir paruošta išankstinio diegimo metudefinite of Laravel, įskaitant patį pirmąjį pagrindinį pavyzdį.

Galite pabandyti įdiegti Laravel projektą ir nedelsdami paleisti pirmuosius testus:

laravel new project
cd project
php artisan test

Tai turėtų būti jūsų konsolės rezultatas:

Jei pažvelgtume į priešdefiLaravelio vakaras /tests, turime du failus:

testai/Feature/ExampleTest.php :

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

Jums nereikia žinoti jokios sintaksės, kad suprastumėte, kas čia vyksta: įkelkite pagrindinį puslapį ir patikrinkite, ar būsenos kodas HTTP è "200 OK".

Taip pat žinomas kaip metodo pavadinimas test_the_application_returns_a_successful_response() tampa skaitomu tekstu, kai žiūrite testo rezultatus, tiesiog pabraukimo simbolį pakeičiant tarpu.

testai/Unit/ExampleTest.php :

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

Atrodo šiek tiek beprasmiška, patikrinti, ar tai tiesa? 

Konkrečiai apie vienetų testus kalbėsime šiek tiek vėliau. Šiuo metu turite suprasti, kas paprastai vyksta kiekviename teste.

  • Kiekvienas bandomasis failas aplanke /tests yra PHP klasė, kuri praplečia TestCase of PHPUnit
  • Kiekvienoje klasėje galite sukurti kelis metodus, dažniausiai vieną metodą situacijai išbandyti
  • Kiekvienas metodas apima tris veiksmus: situacijos paruošimas, tada veiksmas ir patikrinimas (patvirtinimas), ar rezultatas yra toks, kokio tikimasi.

Struktūriškai tai viskas, ką jums reikia žinoti, visa kita priklauso nuo konkrečių dalykų, kuriuos norite išbandyti.

Norėdami sukurti tuščią bandymo klasę, tiesiog paleiskite šią komandą:

php artisan make:test HomepageTest

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

Dabar pažiūrėkime, kas atsitiks, jei Laravel bandomasis kodas nepavyks

Dabar pažiūrėkime, kas nutiks, jei bandymo teiginiai neduos laukiamo rezultato.

Pakeiskime testų pavyzdžius į šiuos:

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

Ir dabar, jei vykdysime komandą php artisan test dar kartą:

 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

Yra du nesėkmingi testai, pažymėti kaip FAIL, toliau pateikiami paaiškinimai ir rodyklės, rodančios tikslią nesėkmingų testų eilutę. Klaidos nurodomos tokiu būdu.

Pavyzdys: registracijos formos kodo testavimas Laravel

Tarkime, kad turime formą ir turime išbandyti įvairius atvejus: patikriname, ar nepavyksta su neteisingais duomenimis, patikriname, ar pavyksta įvedus teisingą įvestį ir pan.

Oficialus pradinis rinkinys pateikė Laravel Breeze apima i joje esančio funkcionalumo išbandymas. Pažvelkime į keletą pavyzdžių iš ten:

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

Čia turime du vienos klasės testus, nes jie abu susiję su registracijos forma: vienas patikrina, ar forma įkelta teisingai, o kitas – ar pateikimas veikia gerai.

Susipažinkime su dar dviem rezultato patikrinimo būdais, dar dviem teiginiais: $this->assertAuthenticated()$response->assertRedirect(). Visus teiginius galite patikrinti oficialiuose dokumentuose PHPUnit e Laravel atsakymas . Atkreipkite dėmesį, kad šiuo klausimu yra keletas bendrų teiginių $this, o kiti tikrina konkretų $responseiš maršruto skambučio.

Kitas svarbus dalykas yra use RefreshDatabase;teiginys su brūkšniu, įterptas virš klasės. Tai būtina, kai bandymo veiksmai gali paveikti duomenų bazę, kaip šiame pavyzdyje, registravimas prideda naują įrašą usersduomenų bazės lentelė. Norėdami tai padaryti, turėtumėte sukurti atskirą bandymų duomenų bazę, kuri bus atnaujinta php artisan migrate:freshkiekvieną kartą, kai atliekami bandymai.

Turite dvi parinktis: fiziškai sukurti atskirą duomenų bazę arba naudoti atmintyje esančią SQLite duomenų bazę. Abu yra sukonfigūruoti faile phpunit.xmlnumatyta pagal nutylėjimądefinita su Laravel. Tiksliau, jums reikia šios dalies:

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

Žiūrėkite DB_CONNECTIONDB_DATABASEkuriuos komentuoja? Jei jūsų serveryje yra SQLite, paprasčiausias veiksmas yra tiesiog panaikinti šių eilučių komentarus ir jūsų testai bus vykdomi pagal tą atmintyje esančią duomenų bazę.

Šiame teste sakome, kad vartotojas sėkmingai autentifikuotas ir nukreipiamas į tinkamą pagrindinį puslapį, tačiau galime patikrinti ir tikrus duomenis duomenų bazėje.

Be šio kodo:

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

Taip pat galime naudoti duomenų bazės testavimo teiginiai ir daryk kažką panašaus:

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

Prisijungimo puslapio pavyzdys

Dabar pažiūrėkime kitą prisijungimo puslapio su Laravel Breeze pavyzdį

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

Tai apie prisijungimo formą. Logika panaši į registraciją, tiesa? Tačiau trys metodai vietoj dviejų, todėl tai yra gerų ir blogų scenarijų testavimo pavyzdys. Taigi, bendra logika yra ta, kad turėtumėte išbandyti abu atvejus: kai viskas klostosi gerai ir kai nepavyksta.

Inovacijų naujienlaiškis
Nepraleiskite svarbiausių naujienų apie naujoves. Prisiregistruokite, kad gautumėte juos el.

Be to, tai, ką matote šiame teste, yra naudojimas Duomenų bazių gamyklos : Laravel sukuria netikrą vartotoją ( dar kartą atnaujintoje bandymų duomenų bazėje ) ir tada bando prisijungti su teisingais arba neteisingais kredencialais.

Dar kartą „Laravel“ sukuria gamyklos išankstinįdefinita su klaidingais duomenimis Usermodelis, išorėje.

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

Matote, kiek dalykų paruošia pats Laravelas, tad ar mums būtų lengva pradėti testuoti?

Taigi, jei vykdysime php artisan testįdiegę Laravel Breeze, turėtume pamatyti kažką panašaus:

 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

Funkciniai testai, palyginti su vienetiniais testais ir kt

Jūs matėte poaplankius tests/Feature e tests/Unit ?. 

Kuo jie skiriasi? 

Pasauliniu mastu, už Laravel/PHP ekosistemos ribų, yra keli automatinio testavimo tipai. Galite rasti tokius terminus kaip:

  • Vienetiniai testai
  • Funkcijų testavimas
  • Integracijos testai
  • Funkciniai testai
  • Visiškas bandymas
  • Priėmimo testai
  • Dūmų testai
  • ir tt

Tai skamba sudėtingai, o tikrieji skirtumai tarp šių tipų testų kartais būna neaiškūs. Štai kodėl Laravel supaprastino visus šiuos painius terminus ir sugrupavo į dvi dalis: vienetas / funkcija.

Paprasčiau tariant, funkcijų testai bando atlikti tikrąsias jūsų programų funkcijas: gauti URL, iškviesti API, imituoti tikslią elgseną, pvz., užpildyti formą. Funkcijų testai paprastai atlieka tas pačias arba panašias operacijas, kaip ir bet kuris projekto vartotojas rankiniu būdu, realiame gyvenime.

Vienetiniai testai turi dvi reikšmes. Apskritai galite pastebėti, kad bet koks automatinis testas vadinamas „vieneto testavimu“, o visas procesas gali būti vadinamas „vieneto testavimu“. Tačiau funkcionalumo ir vieneto kontekste šis procesas yra skirtas konkretaus neviešo kodo vieneto testavimui atskirai. Pavyzdžiui, turite Laravel klasę su metodu, kuris apskaičiuoja kažką, pavyzdžiui, bendrą užsakymo kainą su parametrais. Todėl vieneto testas nurodytų, ar iš to metodo (kodo vieneto) pateikiami teisingi rezultatai su skirtingais parametrais.

Norėdami sugeneruoti vieneto testą, turite pridėti vėliavėlę:

php artisan make:test OrderPriceTest --unit

Sugeneruotas kodas yra toks pat kaip ir išankstinio vieneto testodefiLaravel sistema:

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

Kaip matote, jo nėra RefreshDatabase, ir tai yra vienas iš defiDažniausiai pasitaikantys vieneto testo apibrėžimai: jis neliečia duomenų bazės, veikia kaip „juodoji dėžė“, atskirta nuo veikiančios programos.

Bandydami mėgdžioti anksčiau minėtą pavyzdį, įsivaizduokime, kad turime paslaugų klasę OrderPrice.

app/Services/OrderPriceService.php:

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

Tada vieneto testas gali atrodyti maždaug taip:

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
}

Mano asmeninė patirtis, susijusi su Laravel projektais, didžioji dauguma testų yra funkcijų, o ne vienetų testai. Pirmiausia turite patikrinti, ar jūsų programa veikia taip, kaip ją naudotų tikri žmonės.

Be to, jei turite specialių skaičiavimų ar logikos, galite definire kaip vienetas, su parametrais galite sukurti vienetinius testus specialiai tam.

Kartais, rašant testus, reikia modifikuoti patį kodą ir jį pertvarkyti, kad jis būtų labiau „testuojamas“: padalinti vienetus į specialias klases ar metodus.

Kada/kaip atlikti testus?

Koks yra tikrasis to panaudojimas php artisan test, kada turėtum jį paleisti?

Priklausomai nuo jūsų verslo darbo eigos, yra įvairių metodų, tačiau paprastai turite įsitikinti, kad visi testai yra „žalieji“ (t. y. be klaidų), prieš įkeldami į saugyklą naujausius kodo pakeitimus.

Tada atliekate savo užduotį vietoje, o kai manote, kad baigėte, atlikite keletą testų, kad įsitikintumėte, jog nieko nesugadinote. Atminkite, kad jūsų kodas gali sukelti klaidų ne tik jūsų logikoje, bet ir netyčia sulaužyti kitokią elgseną kažkieno seniai parašytame kode.

Jei žengsime žingsnį toliau, tai įmanoma automatizuoti daug dalykų. Naudodami įvairius CI / CD įrankius galite nurodyti testus, kurie bus vykdomi kaskart, kai kas nors pakeičia konkretų Git šaką arba prieš sujungiant kodą į gamybos šaką. Paprasčiausia darbo eiga būtų naudoti „Github Actions“, turiu atskiras vaizdo įrašas kas tai įrodo.

Ką turėtumėte išbandyti?

Yra įvairių nuomonių, kokio dydžio turėtų būti vadinamoji „bandymo aprėptis“: išbandykite kiekvieną įmanomą operaciją ir atvejį kiekviename puslapyje arba apribokite darbą iki svarbiausių dalių.

Tiesą sakant, čia aš sutinku su žmonėmis, kurie kaltina automatinį testavimą, kad tai užtrunka daugiau laiko nei duoda realios naudos. Taip gali nutikti, jei rašote kiekvienos detalės testus. Beje, to gali prireikti jūsų projektui: pagrindinis klausimas yra „kokia galimos klaidos kaina“.

Kitaip tariant, turite nustatyti pirmenybę savo bandymams, užduodami klausimą „Kas nutiktų, jei šis kodas nepavyktų? Jei jūsų mokėjimo sistemoje yra klaidų, tai turės tiesioginės įtakos verslui. Taigi, jei jūsų vaidmenų / leidimų funkcionalumas yra pažeistas, tai yra didžiulė saugumo problema.

Man patinka, kaip konferencijoje pasakė Mattas Staufferis: „Pirmiausia turite išbandyti tuos dalykus, kurie, jei nepavyktų, jus atleistų iš darbo. Žinoma, tai perdėta, bet supranti: pirmiausia išbandykite svarbius dalykus. Ir kitos funkcijos, jei turite laiko.

PEST: nauja PHPUnit alternatyva

Visi aukščiau pateikti pavyzdžiai yra pagrįsti Laravel išankstinio testavimo įrankiudefivakaras: PHPUnit . Tačiau bėgant metams ekosistemoje atsirado ir kitų priemonių, ir viena iš naujausių populiarių yra KENKLAS . Sukūrė oficialus Laravel darbuotojas Nuno Maduro , siekiama supaprastinti sintaksę, todėl testų kodo rašymas dar greitesnis.

Po gaubtu jis važiuoja su PHPUnit, kaip papildomas sluoksnis, tiesiog bando sumažinti kai kurias iš anksto pasikartojančias dalisdefinite PHPUnit kodą.

Pažiūrėkime į pavyzdį. Prisiminkite išankstinio funkcijų testavimo klasędefinited Laravelyje? priminsiu:

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

Ar žinote, kaip atrodytų tas pats testas su PEST?

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

Taip, VIENA kodo eilutė ir viskas. Taigi, PEST tikslas yra pašalinti pridėtines išlaidas:

  • Kurti klases ir metodus viskam;
  • Bandymo atvejo pratęsimas;
  • Sudėdami veiksmus į atskiras eilutes: PEST galite juos sujungti grandinėmis.

Norėdami sugeneruoti PEST testą Laravel, turite nurodyti papildomą vėliavėlę:

php artisan make:test HomepageTest --pest

Šio rašymo metu PEST yra gana populiarus tarp Laravel kūrėjų, tačiau jūsų asmeninis pasirinkimas, ar naudoti šį papildomą įrankį ir išmokti jo sintaksę, taip pat PHPUnit pastabą.

BlogInnovazione.it

Inovacijų naujienlaiškis
Nepraleiskite svarbiausių naujienų apie naujoves. Prisiregistruokite, kad gautumėte juos el.

Naujausi straipsniai

Vaikų spalvinimo puslapių privalumai – magijos pasaulis įvairaus amžiaus žmonėms

Lavindami smulkiosios motorikos įgūdžius dažydami, vaikai paruošiami sudėtingesniems įgūdžiams, pavyzdžiui, rašymui. Norėdami nuspalvinti…

2 gegužės 2024

Ateitis yra čia: kaip laivybos pramonė sukelia pasaulinės ekonomikos revoliuciją

Karinio jūrų laivyno sektorius yra tikra pasaulinė ekonominė galia, kuri pasiekė 150 mlrd.

1 gegužės 2024

Leidėjai ir OpenAI pasirašo sutartis dėl dirbtinio intelekto apdorojamos informacijos srauto reguliavimo

Praėjusį pirmadienį „Financial Times“ paskelbė apie susitarimą su „OpenAI“. FT licencijuoja savo pasaulinio lygio žurnalistiką…

30 balandis 2024

Mokėjimai internetu: štai kaip srautinio perdavimo paslaugos priverčia mokėti amžinai

Milijonai žmonių moka už srautinio perdavimo paslaugas, mokėdami mėnesinius abonentinius mokesčius. Paplitusi nuomonė, kad jūs…

29 balandis 2024