towary

Dowiedz się, jak wykonywać testy w Laravel na prostych przykładach, używając PHPUnit i PEST

Jeśli chodzi o testy automatyczne lub testy jednostkowe, w dowolnym języku programowania istnieją dwie przeciwstawne opinie:

  • Strata czasu
  • Nie możesz się bez tego obejść

Dlatego w tym artykule postaramy się przekonać tych pierwszych, szczególnie pokazując, jak łatwo jest rozpocząć pracę z automatycznymi testami w Laravel.

Najpierw porozmawiajmy o „dlaczego”, a następnie zobaczmy kilka przykładów tego, jak.

Dlaczego potrzebujemy testów automatycznych

Zautomatyzowane testy uruchamiają części kodu i zgłaszają wszelkie błędy. Tak można je najprościej opisać. Wyobraź sobie, że wdrażasz nową funkcję w aplikacji, a następnie osobisty asystent robota ręcznie testuje nową funkcję, sprawdzając jednocześnie, czy nowy kod nie łamie żadnej ze starych funkcji.

To jest główna zaleta: automatyczne ponowne testowanie wszystkich funkcji. Może się to wydawać dodatkową pracą, ale jeśli nie powiesz „robotowi”, aby to zrobił, powinniśmy to zrobić ręcznie, prawda? 

Można też udostępnić nowe funkcje bez sprawdzania, czy działają, w nadziei, że użytkownicy zgłoszą błędy.

Zautomatyzowane testy mogą dać nam kilka korzyści:

  • Oszczędzaj czas testowania ręcznego;
  • Pozwalają zaoszczędzić czas zarówno na wdrożonej nowej funkcji, jak i na funkcjach skonsolidowanych, unikając regresji;
  • Pomnóż tę korzyść przez wszystkie nowe funkcje i wszystkie funkcje już wdrożone;
  • Poprzednie trzy punkty dotyczą każdej nowej wersji;
  • ...

Spróbuj wyobrazić sobie swoją aplikację za rok lub dwa, z nowymi programistami w zespole, którzy nie znają kodu napisanego w poprzednich latach ani nawet nie wiedzą, jak go przetestować. 

Nasze pierwsze testy automatyczne

Aby wykonać pierwszy automatyczne testowanie w Laravel, nie musisz pisać żadnego kodu. Tak, dobrze to przeczytałeś. Wszystko jest już skonfigurowane i przygotowane w fazie przedinstalacyjnejdeficała Laravel, łącznie z pierwszym podstawowym przykładem.

Możesz spróbować zainstalować projekt Laravel i od razu uruchomić pierwsze testy:

laravel new project
cd project
php artisan test

To powinien być wynik w Twojej konsoli:

Jeśli spojrzymy na przeddefiwieczór Laravela /tests, mamy dwa pliki:

testy/Funkcja/PrzykładTest.php :

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

Nie musisz znać żadnej składni, aby zrozumieć, co się tutaj dzieje: załaduj stronę główną i sprawdź, czy kod statusu HTTP jest "200 OK".

Znana również jako nazwa metody test_the_application_returns_a_successful_response() staje się czytelnym tekstem podczas przeglądania wyników testu, po prostu zastępując symbol podkreślenia spacją.

testy/Jednostka/PrzykładTest.php :

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

Wydaje się to trochę bezcelowe. Sprawdź, czy to prawda? 

Nieco później porozmawiamy konkretnie o testach jednostkowych. Na razie musisz zrozumieć, co zazwyczaj dzieje się w każdym teście.

  • Każdy plik testowy w folderze /tests jest klasą PHP, która rozszerza TestCase of Jednostka PHP
  • W każdej klasie można utworzyć wiele metod, zwykle jedną metodę do testowania sytuacji
  • W ramach każdej metody składają się trzy działania: przygotowanie sytuacji, następnie działanie, a następnie weryfikacja (potwierdzenie), czy wynik jest zgodny z oczekiwaniami

Strukturalnie to wszystko, co musisz wiedzieć, wszystko inne zależy od dokładnych rzeczy, które chcesz przetestować.

Aby wygenerować pustą klasę testową, po prostu uruchom to polecenie:

php artisan make:test HomepageTest

Plik zostanie wygenerowany 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);
    }
}

Zobaczmy teraz, co się stanie, jeśli kod testowy nie powiedzie się w Laravel

Zobaczmy teraz, co się stanie, jeśli twierdzenia testowe nie zwrócą oczekiwanego wyniku.

Zamieńmy przykładowe testy na takie:

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, jeśli uruchomimy polecenie php artisan test Ponownie:

 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

Istnieją dwa testy zakończone niepowodzeniem, oznaczone jako FAIL, z poniższymi wyjaśnieniami i strzałkami wskazującymi dokładną linię testów, które zakończyły się niepowodzeniem. W ten sposób sygnalizowane są błędy.

Przykład: Testowanie kodu formularza rejestracyjnego w Laravel

Załóżmy, że mamy formularz i musimy przetestować różne przypadki: sprawdzamy, czy się nie powiedzie z nieprawidłowymi danymi, sprawdzamy, czy się powiedzie, po wprowadzeniu poprawnych danych itp.

Oficjalny zestaw startowy autorstwa Laravela Breeze’a zawiera ja testowanie jego funkcjonalności. Spójrzmy na kilka przykładów stamtąd:

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

Tutaj mamy dwa testy w jednej klasie, ponieważ oba są powiązane z formularzem rejestracyjnym: jeden sprawdza, czy formularz jest poprawnie załadowany, a drugi sprawdza, czy przesłanie działa prawidłowo.

Zapoznajmy się z jeszcze dwoma metodami weryfikacji wyniku, dwoma kolejnymi twierdzeniami: $this->assertAuthenticated()$response->assertRedirect(). Możesz sprawdzić wszystkie twierdzenia dostępne w oficjalnej dokumentacji Jednostka PHP e Odpowiedź Laravela . Należy zauważyć, że na ten temat pojawiają się pewne ogólne twierdzenia $this, podczas gdy inni sprawdzają specyfikę $responsez wywołania trasy.

Kolejną ważną rzeczą jest use RefreshDatabase;instrukcja z obrysem wstawiona nad klasą. Jest to konieczne, gdy działania testowe mogą mieć wpływ na bazę danych, tak jak w tym przykładzie logowanie dodaje nowy wpis w pliku userstabela bazy danych. W tym celu należy utworzyć oddzielną testową bazę danych, która będzie aktualizowana php artisan migrate:freshza każdym razem, gdy przeprowadzane są testy.

Masz dwie możliwości: fizycznie utworzyć oddzielną bazę danych lub użyć bazy danych SQLite w pamięci. Obydwa są skonfigurowane w pliku phpunit.xmldostarczane domyślniedefiNita z laravel. W szczególności potrzebujesz tej części:

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

Zobacz DB_CONNECTIONDB_DATABASEktóre są komentowane? Jeśli masz SQLite na swoim serwerze, najprostszą czynnością jest po prostu odkomentowanie tych linii, a Twoje testy zostaną uruchomione z tą bazą danych w pamięci.

W tym teście mówimy, że użytkownik został pomyślnie uwierzytelniony i przekierowany na właściwą stronę główną, ale możemy również przetestować rzeczywiste dane w bazie danych.

Oprócz tego kodu:

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

Możemy również skorzystać asercje testowe bazy danych i zrób coś takiego:

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

Przykład strony logowania

Zobaczmy teraz inny przykład strony logowania 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();
    }
}

Chodzi o formularz logowania. Logika jest podobna do rejestracji, prawda? Ale trzy metody zamiast dwóch, więc jest to przykład testowania zarówno dobrych, jak i złych scenariuszy. Zatem powszechna logika jest taka, że ​​należy przetestować oba przypadki: kiedy wszystko idzie dobrze, i kiedy się nie udaje.

Biuletyn innowacji
Nie przegap najważniejszych wiadomości dotyczących innowacji. Zarejestruj się, aby otrzymywać je e-mailem.

Ponadto w tym teście widać użycie Fabryki baz danych : Laravel tworzy fałszywego użytkownika ( ponownie w zaktualizowanej testowej bazie danych ), a następnie próbuje się zalogować, podając poprawne lub niepoprawne dane uwierzytelniające.

Po raz kolejny Laravel generuje fabryczny plik predefinita z fałszywymi danymi dla Usermodel, poza pudełkiem.

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

Widzisz, ile rzeczy przygotowuje sam Laravel, więc czy łatwo byłoby nam zacząć testować?

Jeśli więc wykonamy php artisan testpo zainstalowaniu Laravel Breeze powinniśmy zobaczyć coś takiego:

 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

Testy funkcjonalne w porównaniu z testami jednostkowymi i innymi

Widziałeś podfoldery tests/Feature e tests/Unit ?. 

Jaka jest różnica między nimi? 

Globalnie, poza ekosystemem Laravel/PHP, istnieje kilka rodzajów testów automatycznych. Można znaleźć takie terminy jak:

  • Testy jednostkowe
  • Testowanie funkcji
  • Testy integracyjne
  • Testy funkcjonalne
  • Testowanie od końca do końca
  • Test wstępny
  • Testy dymu
  • itp.

Brzmi to skomplikowanie, a faktyczne różnice pomiędzy tego typu testami czasami się zacierają. Dlatego Laravel uprościł wszystkie te mylące terminy i pogrupował je w dwie części: jednostka/cecha.

Mówiąc najprościej, testy funkcjonalności próbują wykonać rzeczywistą funkcjonalność aplikacji: uzyskać adres URL, wywołać API, naśladować dokładne zachowanie, takie jak wypełnienie formularza. Testy funkcji zwykle wykonują te same lub podobne operacje, które każdy użytkownik projektu wykonałby ręcznie w prawdziwym życiu.

Testy jednostkowe mają dwa znaczenia. Ogólnie rzecz biorąc, może się okazać, że każdy test automatyczny nazywany jest „testowaniem jednostkowym”, a cały proces można nazwać „testowaniem jednostkowym”. Jednak w kontekście funkcjonalności i jednostki proces ten polega na testowaniu konkretnej, niepublicznej jednostki kodu, w izolacji. Na przykład masz klasę Laravel z metodą, która coś oblicza, na przykład całkowitą cenę zamówienia z parametrami. Dlatego test jednostkowy określiłby, czy z tej metody (jednostki kodu) zwracane są prawidłowe wyniki z różnymi parametrami.

Aby wygenerować test jednostkowy, musisz dodać flagę:

php artisan make:test OrderPriceTest --unit

Wygenerowany kod jest taki sam, jak przed testem jednostkowymdefiSystem Laravel:

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

Jak widać nie istnieje RefreshDatabase, a to jest jeden z definajczęstsze definicje testów jednostkowych: nie dotykają bazy danych, działają jak „czarna skrzynka”, odizolowana od działającej aplikacji.

Próbując naśladować przykład, o którym wspomniałem wcześniej, wyobraźmy sobie, że mamy klasę usług OrderPrice.

app/Services/OrderPriceService.php:

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

Następnie test jednostkowy mógłby wyglądać mniej więcej tak:

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
}

Z mojego osobistego doświadczenia z projektami Laravel wynika, że ​​zdecydowana większość testów to testy funkcjonalności, a nie testy jednostkowe. Najpierw musisz przetestować, czy Twoja aplikacja działa i jak będą z niej korzystać prawdziwi ludzie.

Następnie, jeśli masz specjalne obliczenia lub logikę, możesz to zrobić definire jako jednostka, z parametrami, możesz stworzyć specjalnie do tego testy jednostkowe.

Czasami pisanie testów wymaga modyfikacji samego kodu i refaktoryzacji go, aby uczynić go bardziej „testowalnym”: podzielenia jednostek na specjalne klasy lub metody.

Kiedy/jak wykonywać badania?

Jakie jest tego faktyczne zastosowanie php artisan test, kiedy należy go uruchomić?

Istnieją różne podejścia, w zależności od przepływu pracy w Twojej firmie, ale generalnie musisz upewnić się, że wszystkie testy są „ekologiczne” (tj. wolne od błędów) przed wypchnięciem ostatecznych zmian w kodzie do repozytorium.

Następnie pracujesz lokalnie nad swoim zadaniem, a kiedy uznasz, że skończyłeś, przeprowadź kilka testów, aby upewnić się, że niczego nie zepsułeś. Pamiętaj, że Twój kod może powodować błędy nie tylko w logice, ale także w sposób niezamierzony zakłócać inne zachowania w kodzie napisanym dawno temu przez inną osobę.

Jeśli pójdziemy o krok dalej, możliwa jest automatyzacja wiele rzeczy. Dzięki różnym narzędziom CI/CD możesz określić testy, które będą uruchamiane za każdym razem, gdy ktoś prześle zmiany do określonej gałęzi Git lub przed połączeniem kodu z gałęzią produkcyjną. Najprostszym przepływem pracy byłoby użycie Github Actions, tak mam osobny film co to potwierdza.

Co warto przetestować?

Istnieją różne opinie na temat tego, jak duży powinien być tzw. „pokrycie testowe”: wypróbuj każdą możliwą operację i przypadek na każdej stronie lub ogranicz pracę do najważniejszych części.

Właściwie w tym miejscu zgadzam się z osobami, które zarzucają, że automatyczne testy zajmują więcej czasu niż przynoszą rzeczywiste korzyści. Może się to zdarzyć, jeśli napiszesz testy dla każdego szczegółu. To powiedziawszy, może być to wymagane przez Twój projekt: główne pytanie brzmi: „jaka jest cena potencjalnego błędu”.

Innymi słowy, musisz ustalić priorytety swoich wysiłków testowych, zadając pytanie „Co by się stało, gdyby ten kod się nie powiódł?” Jeśli w Twoim systemie płatności występują błędy, będzie to miało bezpośredni wpływ na działalność firmy. Jeśli więc funkcjonalność Twoich ról/uprawnień zostanie zepsuta, jest to poważny problem związany z bezpieczeństwem.

Podoba mi się, jak Matt Stauffer ujął to na konferencji: „Najpierw musisz przetestować te rzeczy, które, jeśli zawiodą, spowodują wyrzucenie z pracy”. Oczywiście to przesada, ale rozumiesz, o co chodzi: najpierw wypróbuj ważne rzeczy. A potem inne funkcje, jeśli masz czas.

PEST: nowa alternatywa dla PHPUnit

Wszystkie powyższe przykłady opierają się na narzędziu do wstępnego testowania Laraveldefinoc: Jednostka PHP . Jednak z biegiem lat w ekosystemie pojawiły się inne narzędzia, a jednym z najnowszych i popularnych jest PEST . Stworzony przez oficjalnego pracownika Laravel Nuna Maduro , ma na celu uproszczenie składni, dzięki czemu pisanie kodu do testów jest jeszcze szybsze.

Pod maską chodzi su PHPUnit, jako dodatkowa warstwa, po prostu próbując zminimalizować niektóre wcześniej powtarzane częścidefikoniec kodu PHPUnit.

Spójrzmy na przykład. Pamiętaj o zajęciach z testu wstępnego funkcjidefipołączone w Laravel? Przypomnę Ci:

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

Czy wiesz, jak wyglądałby ten sam test z PEST?

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

Tak, JEDNA linia kodu i to wszystko. Zatem celem PEST jest usunięcie narzutu:

  • Tworzenie klas i metod do wszystkiego;
  • Rozszerzenie przypadku testowego;
  • Umieszczając akcje w oddzielnych liniach: w PEST możesz je połączyć.

Aby wygenerować test PEST w Laravel należy podać dodatkową flagę:

php artisan make:test HomepageTest --pest

W chwili pisania tego tekstu PEST jest dość popularny wśród programistów Laravel, ale to od Ciebie zależy, czy użyjesz tego dodatkowego narzędzia i poznasz jego składnię, a także uwagę dotyczącą PHPUnit.

BlogInnovazione.it

Biuletyn innowacji
Nie przegap najważniejszych wiadomości dotyczących innowacji. Zarejestruj się, aby otrzymywać je e-mailem.

Najnowsze artykuły

Jak korzystać z widoków i układu w programie PowerPoint

Microsoft PowerPoint udostępnia różne typy narzędzi, dzięki którym prezentacje stają się użyteczne, interaktywne i odpowiednie do różnych celów. Instrumenty…

20 maja 2024

Uczenie maszynowe: porównanie losowego lasu i drzewa decyzyjnego

W świecie uczenia maszynowego zarówno algorytmy lasów losowych, jak i algorytmy drzew decyzyjnych odgrywają istotną rolę w kategoryzacji i…

17 maja 2024

Jak ulepszyć prezentacje Power Point, przydatne wskazówki

Istnieje wiele wskazówek i wskazówek dotyczących tworzenia świetnych prezentacji. Celem tych zasad jest poprawa efektywności, płynności…

16 maja 2024

Jak wynika z raportu Protolabs, prędkość nadal jest dźwignią w rozwoju produktów

Opublikowano raport „Perspektywy rozwoju produktu Protolabs”. Sprawdź, w jaki sposób nowe produkty są dziś wprowadzane na rynek.…

16 maja 2024

Cztery filary zrównoważonego rozwoju

Termin zrównoważony rozwój jest obecnie powszechnie używany do określenia programów, inicjatyw i działań mających na celu ochronę określonego zasobu.…

15 maja 2024

Jak konsolidować dane w Excelu

Każda operacja biznesowa generuje mnóstwo danych, nawet w różnych formach. Wprowadź ręcznie te dane z arkusza Excel, aby…

14 maja 2024

Kwartalna analiza Cisco Talos: firmowe wiadomości e-mail będące celem przestępców to najbardziej dotknięte sektory: produkcja, edukacja i opieka zdrowotna

W pierwszych trzech miesiącach 2024 r. w porównaniu z ostatnim kwartałem XNUMX r. liczba zainfekowanych e-maili firmowych wzrosła ponad dwukrotnie.

14 maja 2024

Zasada segregacji interfejsu (ISP), czwarta zasada SOLID

Zasada segregacji interfejsów jest jedną z pięciu zasad SOLID projektowania obiektowego. Klasa powinna mieć…

14 maja 2024

Przeczytaj Innowacje w swoim języku

Biuletyn innowacji
Nie przegap najważniejszych wiadomości dotyczących innowacji. Zarejestruj się, aby otrzymywać je e-mailem.

Śledź nas