Artikels

Leer hoe om toetse in Laravel te doen met eenvoudige voorbeelde, met PHPUnit en PEST

Wanneer dit kom by outomatiese toetse of eenheidstoetse, in enige programmeertaal, is daar twee opponerende menings:

  • Mors van tyd
  • Jy kan nie daarsonder nie

Dus, met hierdie artikel sal ons eersgenoemde probeer oortuig, veral deur te demonstreer hoe maklik dit is om met outomatiese toetsing in Laravel te begin.

Kom ons praat eers oor die "hoekom", en laat ons dan 'n paar voorbeelde van die hoe sien.

Waarom ons outomatiese toetsing nodig het

Outomatiese toetse voer dele van die kode uit en rapporteer enige foute. Dit is die eenvoudigste manier om hulle te beskryf. Stel jou voor dat jy 'n nuwe kenmerk in 'n toepassing begin, en dan sal 'n persoonlike robotassistent die nuwe kenmerk handmatig gaan toets, terwyl dit ook toets of die nuwe kode nie enige van die ou kenmerke breek nie.

Dit is die grootste voordeel: hertoets alle kenmerke outomaties. Dit lyk dalk na ekstra werk, maar as jy nie vir die “robot” sê om dit te doen nie, moet ons dit alternatiewelik met die hand doen, reg? 

Of nuwe kenmerke kan vrygestel word sonder om te toets of dit werk, met die hoop dat gebruikers foute sal rapporteer.

Outomatiese toetse kan ons verskeie voordele gee:

  • Bespaar handmatige toetstyd;
  • Hulle laat jou toe om tyd te bespaar op beide die nuwe funksie wat geïmplementeer is en op die gekonsolideerde funksies deur regressie te vermy;
  • Vermenigvuldig hierdie voordeel met alle nuwe kenmerke en alle kenmerke wat reeds geïmplementeer is;
  • Die vorige drie punte is van toepassing op elke nuwe weergawe;
  • ...

Probeer om jou toepassing oor 'n jaar of twee voor te stel, met nuwe ontwikkelaars in die span wat nie die kode ken wat in vorige jare geskryf is nie, of selfs hoe om dit te toets. 

Ons eerste outomatiese toetse

Om die eerste uit te voer outomatiese toetsing in Laravel, hoef jy geen kode te skryf nie. Ja, jy het reg gelees. Alles is reeds in die vooraf-installasie gekonfigureer en voorbereidefiLaravel, insluitend die heel eerste basiese voorbeeld.

Jy kan probeer om 'n Laravel-projek te installeer en die eerste toetse onmiddellik uit te voer:

laravel new project
cd project
php artisan test

Dit behoort die resultaat in jou konsole te wees:

As ons kyk na die predefinag van Laravel /tests, ons het twee lêers:

toetse/Feature/ExampleTest.php :

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

Jy hoef nie enige sintaksis te ken om te verstaan ​​wat hier aangaan nie: laai die tuisblad en kyk of die statuskode HTTP è "200 OK".

Ook bekend as die metode naam test_the_application_returns_a_successful_response() word leesbare teks wanneer jy die toetsresultate bekyk, bloot deur die onderstreepsimbool met 'n spasie te vervang.

tests/Unit/ExampleTest.php :

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

Lyk 'n bietjie sinneloos, om te kyk of dit waar is? 

Ons sal 'n bietjie later spesifiek oor eenheidstoetse praat. Vir nou moet jy verstaan ​​wat oor die algemeen in elke toets gebeur.

  • Elke toetslêer in die gids /tests is 'n PHP-klas wat die TestCase van uitbrei PHPEenheid
  • Binne elke klas kan jy verskeie metodes skep, gewoonlik een metode vir 'n situasie om te toets
  • Binne elke metode is daar drie aksies: voorbereiding van die situasie, dan aksie en dan verifieer (bevestig) of die uitkoms soos verwag is

Struktureel is dit al wat jy moet weet, alles hang af van die presiese dinge wat jy wil toets.

Om 'n leë toetsklas te genereer, voer eenvoudig hierdie opdrag uit:

php artisan make:test HomepageTest

Die lêer word gegenereer 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);
    }
}

Kom ons kyk nou wat gebeur as 'n toetskode in Laravel misluk

Kom ons kyk nou wat gebeur as die toetsstellings nie die verwagte resultaat lewer nie.

Kom ons verander die voorbeeldtoetse na hierdie:

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

En nou, as ons die opdrag uitvoer php artisan test weer:

 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

Daar is twee mislukte toetse, gemerk as FAIL, met verduidelikings hieronder en pyltjies wat na die presiese lyn van toetse wys wat gedruip het. Foute word op hierdie manier aangedui.

Voorbeeld: Toets registrasievormkode in Laravel

Gestel ons het 'n vorm en ons moet verskeie gevalle toets: ons kyk of dit misluk met ongeldige data, ons kyk of dit met die korrekte invoer slaag, ens.

Die amptelike beginpakket deur Laravel Breeze sluit i die funksionaliteit daarin te toets. Kom ons kyk na 'n paar voorbeelde daarvandaan:

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

Hier het ons twee toetse in een klas, aangesien hulle albei verband hou met die registrasievorm: een kyk of die vorm korrek gelaai is en 'n ander kyk of die indiening goed werk.

Kom ons raak vertroud met nog twee metodes om die resultaat te verifieer, nog twee bewerings: $this->assertAuthenticated()$response->assertRedirect(). U kan al die stellings wat beskikbaar is in die amptelike dokumentasie van PHPEenheid e Laravel-reaksie . Let daarop dat sommige algemene bewerings oor die onderwerp voorkom $this, terwyl ander die spesifieke nagaan $responsevanaf die roete-oproep.

Nog 'n belangrike ding is die use RefreshDatabase;verklaring, met die beroerte, bo die klas ingevoeg. Dit is nodig wanneer toetsaksies die databasis kan beïnvloed, soos in hierdie voorbeeld, logging voeg 'n nuwe inskrywing in die usersdatabasis tabel. Hiervoor moet jy 'n aparte toetsdatabasis skep waarmee bygewerk sal word php artisan migrate:freshelke keer as die toetse uitgevoer word.

Jy het twee opsies: skep fisies 'n aparte databasis of gebruik 'n in-geheue SQLite databasis. Albei is in die lêer gekonfigureer phpunit.xmlby verstek verskafdefinita met Laravel. Spesifiek, jy benodig hierdie deel:

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

Sien die DB_CONNECTIONDB_DATABASEop watter word kommentaar gelewer? As jy SQLite op jou bediener het, is die eenvoudigste aksie om eenvoudig daardie reëls te ontmerk en jou toetse sal teen daardie in-geheue databasis loop.

In hierdie toets sê ons dat die gebruiker suksesvol geverifieer en na die korrekte tuisblad herlei is, maar ons kan ook die werklike data in die databasis toets.

Benewens hierdie kode:

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

Ons kan ook gebruik die databasistoetsstellings en doen so iets:

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

Voorbeeld van die aanmeldbladsy

Kom ons kyk nou na nog 'n voorbeeld van 'n aanmeldbladsy met 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();
    }
}

Dit gaan oor die aanmeldvorm. Die logika is soortgelyk aan registrasie, reg? Maar drie metodes in plaas van twee, so dit is 'n voorbeeld van die toets van beide goeie en slegte scenario's. Dus, die algemene logika is dat jy albei gevalle moet toets: wanneer dinge goed gaan en wanneer dit misluk.

Innovasie nuusbrief
Moenie die belangrikste nuus oor innovasie mis nie. Registreer om hulle per e-pos te ontvang.

Wat jy ook in hierdie toets sien, is die gebruik van Databasisfabrieke : Laravel skep vals gebruiker ( weer op jou opgedateerde toetsdatabasis ) en probeer dan om aan te meld, met korrekte of verkeerde geloofsbriewe.

Weereens genereer Laravel die fabriek predefinita met vals data vir die Usermodel, buite die boks.

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

Jy sien, hoeveel dinge word deur Laravel self voorberei, so sal dit vir ons maklik wees om te begin toets?

So as ons uitvoer php artisan testnadat ons Laravel Breeze geïnstalleer het, behoort ons iets soos hierdie te sien:

 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

Funksionele toetse in vergelyking met eenheidstoetse en ander

Jy het die subgidse gesien tests/Feature e tests/Unit ?. 

Wat is die verskil tussen hulle? 

Wêreldwyd, buite die Laravel/PHP-ekosisteem, is daar verskeie tipes outomatiese toetsing. Jy kan terme vind soos:

  • Eenheid toetse
  • Funksietoetsing
  • Integrasie toetse
  • Funksionele toetse
  • End-tot-end toetsing
  • Aanvaardingstoetse
  • Rook toetse
  • ens.

Dit klink ingewikkeld, en die werklike verskille tussen hierdie tipe toetse is soms vervaag. Daarom het Laravel al hierdie verwarrende terme vereenvoudig en in twee gegroepeer: eenheid/kenmerk.

Eenvoudig gestel, kenmerktoetse probeer om die werklike funksionaliteit van jou toepassings uit te voer: kry die URL, bel die API, boots die presiese gedrag na soos om die vorm in te vul. Kenmerktoetse voer gewoonlik dieselfde of soortgelyke bewerkings uit as wat enige projekgebruiker handmatig in die werklike lewe sou doen.

Eenheidstoetse het twee betekenisse. Oor die algemeen kan jy vind dat enige outomatiese toets "eenheidstoetsing" genoem word en die hele proses kan "eenheidstoetsing" genoem word. Maar in die konteks van funksionaliteit versus eenheid, gaan hierdie proses oor die toets van 'n spesifieke nie-openbare eenheid van kode, in isolasie. Byvoorbeeld, jy het 'n Laravel-klas met 'n metode wat iets bereken, soos die totale bestelprys met parameters. Daarom sal die eenheidstoets aandui of korrekte resultate van daardie metode (kode-eenheid) met verskillende parameters teruggestuur word.

Om 'n eenheidstoets te genereer, moet jy 'n vlag byvoeg:

php artisan make:test OrderPriceTest --unit

Die gegenereerde kode is dieselfde as die pre-eenheid toetsdefiLaravel stelsel:

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

Soos jy kan sien, bestaan ​​dit nie RefreshDatabase, en dit is een van defimees algemene eenheidstoetsdefinisies: dit raak nie aan die databasis nie, dit werk as 'n "swart boks", geïsoleer van die lopende toepassing.

Om die voorbeeld wat ek vroeër genoem het te probeer naboots, laat ons ons verbeel ons het 'n diensklas OrderPrice.

app/Services/OrderPriceService.php:

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

Dan kan die eenheidstoets iets soos volg lyk:

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
}

In my persoonlike ervaring met Laravel-projekte is die oorgrote meerderheid van die toetse kenmerktoetse, nie eenheidstoetse nie. Eerstens moet jy toets of jou toepassing werk, soos regte mense dit sal gebruik.

Volgende, as jy spesiale berekeninge of logika het, kan jy definire as 'n eenheid, met parameters, kan jy eenheidstoetse spesifiek daarvoor skep.

Soms vereis die skryf van toetse om die kode self te wysig en dit te herfaktoreer om dit meer "toetsbaar" te maak: die eenhede te skei in spesiale klasse of metodes.

Wanneer/hoe om toetse uit te voer?

Wat is die werklike nut hiervan php artisan test, wanneer moet jy dit laat loop?

Daar is verskillende benaderings, afhangende van jou besigheidswerkvloei, maar oor die algemeen moet jy seker maak dat alle toetse "groen" is (d.w.s. foutvry) voordat jy die nuutste kodeveranderinge na die bewaarplek stoot.

Dan werk jy plaaslik aan jou taak, en wanneer jy dink jy is klaar, voer 'n paar toetse uit om seker te maak jy het niks gebreek nie. Onthou, jou kode kan foute veroorsaak, nie net in jou logika nie, maar ook onbedoeld ander gedrag in iemand anders se kode wat lank gelede geskryf is, breek.

As ons dit 'n stap verder neem, is dit moontlik om te outomatiseer baie dinge. Met verskeie CI/CD-nutsmiddels kan u toetse spesifiseer wat uitgevoer moet word wanneer iemand veranderinge aan 'n spesifieke Git-tak druk of voordat kode in die produksietak saamgevoeg word. Die eenvoudigste werkvloei sou wees om Github Actions te gebruik, ek het 'n aparte video wat dit bewys.

Wat moet jy toets?

Daar is verskillende menings oor hoe groot die sogenaamde "toetsdekking" moet wees: probeer elke moontlike bewerking en geval op elke bladsy, of beperk die werk tot die belangrikste dele.

Trouens, dit is waar ek saamstem met mense wat geoutomatiseerde toetsing daarvan beskuldig dat dit meer tyd neem as om werklike voordeel te bied. Dit kan gebeur as jy toetse skryf vir elke detail. Dit gesê, dit kan deur u projek vereis word: die hoofvraag is "wat is die prys van potensiële foute".

Met ander woorde, jy moet jou toetspogings prioritiseer deur die vraag te vra "Wat sal gebeur as hierdie kode misluk?" As jou betalingstelsel foute het, sal dit die besigheid direk beïnvloed. So as die funksionaliteit van jou rolle/toestemmings gebreek is, is dit 'n groot sekuriteitsprobleem.

Ek hou van hoe Matt Stauffer dit by 'n konferensie gestel het: "Jy moet eers daardie dinge toets wat, as hulle misluk, jou van jou werk sou ontslaan." Dit is natuurlik 'n oordrywing, maar jy kry die idee: probeer eers die belangrike goed. En dan ander kenmerke, as jy tyd het.

PEST: nuwe alternatief vir PHPUnit

Al die bogenoemde voorbeelde is gebaseer op die Laravel-voortoetsinstrumentdefinag: PHPEenheid . Maar oor die jare het ander instrumente in die ekosisteem verskyn en een van die nuutste gewilde is PLAAG . Geskep deur amptelike Laravel-werknemer Nuno Maduro , het ten doel om die sintaksis te vereenvoudig, wat die skryf van kode vir toetse selfs vinniger maak.

Onder die enjinkap loop dit su PHPUnit, as 'n bykomende laag, probeer net om sommige voorafherhaalde dele te minimaliseerdefiniteit van die PHPUnit-kode.

Kom ons kyk na 'n voorbeeld. Onthou die voorkenmerktoetsklasdefiin Laravel gesit? Ek sal jou herinner:

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

Weet jy hoe dieselfde toets met PEST sou lyk?

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

Ja, EEN reël kode en dit is dit. Dus, die doel van PEST is om die bokoste van:

  • Die skep van klasse en metodes vir alles;
  • Uitbreiding van toetsgevalle;
  • Deur aksies op aparte lyne te plaas: in PEST kan jy hulle aanmekaar ketting.

Om 'n PEST-toets in Laravel te genereer, moet jy 'n bykomende vlag spesifiseer:

php artisan make:test HomepageTest --pest

Soos hierdie skrywe is, is PEST baie gewild onder Laravel-ontwikkelaars, maar dit is jou persoonlike voorkeur of jy hierdie bykomende instrument moet gebruik en die sintaksis daarvan moet leer, sowel as 'n PHPUnit-noot.

BlogInnovazione.it

Innovasie nuusbrief
Moenie die belangrikste nuus oor innovasie mis nie. Registreer om hulle per e-pos te ontvang.

Onlangse artikels

Die voordele van inkleurbladsye vir kinders - 'n wêreld van magie vir alle ouderdomme

Die ontwikkeling van fyn motoriese vaardighede deur inkleur berei kinders voor vir meer komplekse vaardighede soos skryf. Om in te kleur...

2 Mei 2024

Die toekoms is hier: hoe die verskepingsbedryf die wêreldekonomie revolusioneer

Die vlootsektor is 'n ware globale ekonomiese moondheid, wat na 'n 150 miljard-mark navigeer het ...

1 Mei 2024

Uitgewers en OpenAI onderteken ooreenkomste om die vloei van inligting wat deur Kunsmatige Intelligensie verwerk word, te reguleer

Verlede Maandag het die Financial Times 'n ooreenkoms met OpenAI aangekondig. FT lisensieer sy wêreldklas-joernalistiek ...

30 April 2024

Aanlynbetalings: Hier is hoe stroomdienste jou vir altyd laat betaal

Miljoene mense betaal vir stromingsdienste en betaal maandelikse intekengeld. Dit is algemene opinie dat jy...

29 April 2024