Когато става въпрос за автоматизирани тестове или модулни тестове, във всеки език за програмиране има две противоположни мнения:
Така че с тази статия ще се опитаме да убедим първото, особено като демонстрираме колко лесно е да започнете с автоматизирано тестване в Laravel.
Първо нека поговорим за „защо“, а след това нека видим някои примери за това как.
Автоматизираните тестове изпълняват части от кода и докладват всички грешки. Това е най-простият начин да ги опишем. Представете си да пуснете нова функция в приложение и след това личен асистент робот ще отиде и ще тества ръчно новата функция, като същевременно ще тества дали новият код не нарушава някоя от старите функции.
Това е основното предимство: повторно тестване на всички функции автоматично. Това може да изглежда като допълнителна работа, но ако не кажете на „робота“ да го направи, алтернативно трябва да го направим ръчно, нали?
Или могат да бъдат пуснати нови функции, без да се тества дали работят, с надеждата, че потребителите ще докладват грешки.
Автоматизираните тестове могат да ни дадат няколко предимства:
Опитайте се да си представите вашето приложение след година или две, с нови разработчици в екипа, които не знаят кода, написан през предишни години, или дори как да го тестват.
За изпълнение на първото автоматизирано тестване в Laravel, не е необходимо да пишете никакъв код. Да, правилно прочетохте. Всичко вече е конфигурирано и подготвено в предварителната инсталацияdefiкрая на Laravel, включително първия основен пример.
Можете да опитате да инсталирате проект на Laravel и да стартирате първите тестове веднага:
laravel new project
cd project
php artisan test
Това трябва да е резултатът във вашата конзола:
Ако разгледаме преdefiкрая на Laravel /tests
, имаме два файла:
tests/Feature/ExampleTest.php :
class ExampleTest extends TestCase
{
public function test_the_application_returns_a_successful_response()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
Не е необходимо да знаете синтаксис, за да разберете какво се случва тук: заредете началната страница и проверете дали кодът на състоянието HTTP
è "200 OK
".
Известен също като име на метод test_the_application_returns_a_successful_response()
става четим текст, когато видите резултатите от теста, просто като замените символа за подчертаване с интервал.
tests/Unit/ExampleTest.php :
class ExampleTest extends TestCase
{
public function test_that_true_is_true()
{
$this->assertTrue(true);
}
}
Изглежда малко безсмислено да проверявате дали това е вярно?
Малко по-късно ще говорим специално за тестовете на модулите. Засега трябва да разберете какво обикновено се случва във всеки тест.
/tests
е PHP клас, който разширява TestCase на PHPUnitСтруктурно, това е всичко, което трябва да знаете, всичко останало зависи от точните неща, които искате да тествате.
За да генерирате празен тестов клас, просто изпълнете тази команда:
php artisan make:test HomepageTest
Файлът се генерира 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);
}
}
Нека сега да видим какво се случва, ако тестовите твърдения не върнат очаквания резултат.
Нека променим примерните тестове на това:
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);
}
}
И сега, ако изпълним командата php artisan test
отново:
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
Има два неуспешни теста, маркирани като НЕУСПЕШЕН, с обяснения по-долу и стрелки, сочещи към точния ред от неуспешни тестове. Грешките се обозначават по този начин.
Да предположим, че имаме формуляр и трябва да тестваме различни случаи: проверяваме дали е неуспешен с невалидни данни, проверяваме дали е успешен с правилния вход и т.н.
Официалният стартов комплект от Laravel Breeze включва i тестване на функционалността в него. Нека да разгледаме някои примери от там:
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);
}
}
Тук имаме два теста в един клас, тъй като и двата са свързани с формуляра за регистрация: един проверява дали формулярът е зареден правилно, а друг проверява дали изпращането работи добре.
Нека се запознаем с още два метода за проверка на резултата, още две твърдения: $this->assertAuthenticated()
e $response->assertRedirect()
. Можете да проверите всички налични твърдения в официалната документация на PHPUnit e Отговор на Laravel . Обърнете внимание, че се срещат някои общи твърдения по темата $this
, докато други проверяват конкретните $response
от обаждането на маршрута.
Друго важно нещо е use RefreshDatabase;
израз, с щрих, вмъкнат над класа. Необходимо е, когато тестовите действия могат да повлияят на базата данни, както в този пример, регистрирането добавя нов запис в users
таблица на база данни. За целта трябва да създадете отделна тестова база данни, която ще се актуализира с php artisan migrate:fresh
всеки път, когато се изпълняват тестовете.
Имате две възможности: физически да създадете отделна база данни или да използвате SQLite база данни в паметта. И двете са конфигурирани във файла phpunit.xml
предоставени по подразбиранеdefiнита с Laravel. По-конкретно, имате нужда от тази част:
<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>
Вижте DB_CONNECTION
e DB_DATABASE
кои се коментират? Ако имате SQLite на вашия сървър, най-простото действие е просто да премахнете коментарите от тези редове и вашите тестове ще се изпълняват срещу тази база данни в паметта.
В този тест казваме, че потребителят е успешно удостоверен и пренасочен към правилната начална страница, но можем също да тестваме действителните данни в базата данни.
В допълнение към този код:
$this->assertAuthenticated();
$response->assertRedirect(RouteServiceProvider::HOME);
Можем също да използваме твърденията за тестване на базата данни и направете нещо подобно:
$this->assertDatabaseCount('users', 1);
// Or...
$this->assertDatabaseHas('users', [
'email' => 'test@example.com',
]);
Нека сега видим друг пример за страница за вход с 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();
}
}
Става въпрос за формата за вход. Логиката е подобна на регистрацията, нали? Но три метода вместо два, така че това е пример за тестване на добри и лоши сценарии. Така че общата логика е, че трябва да тествате и двата случая: когато нещата вървят добре и когато се провалят.
Освен това това, което виждате в този тест, е употребата на Фабрики за бази данни : Laravel създава фалшив потребител ( отново във вашата актуализирана тестова база данни ) и след това се опитва да влезе с правилни или неправилни идентификационни данни.
Още веднъж, Laravel генерира фабричните predefiнита с неверни данни за User
модел, извън кутията.
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),
];
}
}
Виждате ли, колко много неща са подготвени от самия Laravel, така че лесно ли ще ни бъде да започнем да тестваме?
Така че, ако изпълним php artisan test
след като инсталираме Laravel Breeze, трябва да видим нещо подобно:
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
Видяхте подпапките tests/Feature
e tests/Unit
?.
Каква е разликата между тях?
В световен мащаб, извън екосистемата Laravel/PHP, има няколко вида автоматизирано тестване. Можете да намерите термини като:
Звучи сложно и действителните разлики между тези видове тестове понякога са замъглени. Ето защо Laravel опрости всички тези объркващи термини и ги групира в две: единица/функция.
Просто казано, тестовете на функции се опитват да изпълнят действителната функционалност на вашите приложения: вземете URL адреса, извикайте API, имитирайте точното поведение като попълване на формуляра. Тестовете на функциите обикновено извършват същите или подобни операции, както всеки потребител на проект би направил ръчно в реалния живот.
Единичните тестове имат две значения. Като цяло може да откриете, че всеки автоматизиран тест се нарича „единично тестване“ и целият процес може да се нарече „единично тестване“. Но в контекста на функционалност срещу единица, този процес е за тестване на конкретна непублична единица код, в изолация. Например, имате клас Laravel с метод, който изчислява нещо, като общата цена на поръчката с параметри. Следователно единичният тест ще посочи дали са върнати правилни резултати от този метод (кодова единица) с различни параметри.
За да генерирате единичен тест, трябва да добавите флаг:
php artisan make:test OrderPriceTest --unit
Генерираният код е същият като преди модулния тестdefiLaravel система:
class OrderPriceTest extends TestCase
{
public function test_example()
{
$this->assertTrue(true);
}
}
Както виждате, не съществува RefreshDatabase
, а това е едно от defiнай-често срещаните дефиниции на модулен тест: не докосва базата данни, работи като „черна кутия“, изолирана от работещото приложение.
Опитвайки се да имитираме примера, който споменах по-рано, нека си представим, че имаме клас на обслужване OrderPrice
.
app/Services/OrderPriceService.php:
class OrderPriceService
{
public function calculatePrice($productId, $quantity, $tax = 0.0)
{
// Some kind of calculation logic
}
}
Тогава единичният тест може да изглежда така:
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
}
Според моя личен опит с проекти на Laravel, по-голямата част от тестовете са Feature tests, а не Unit tests. Първо, трябва да тествате дали вашето приложение работи по начина, по който реалните хора биха го използвали.
След това, ако имате специални изчисления или логика, можете definire като единица, с параметри, можете да създавате единици тестове специално за това.
Понякога писането на тестове изисква модифициране на самия код и преработването му, за да стане по-тестваем: разделяне на единиците в специални класове или методи.
Каква е действителната полза от това php artisan test
, кога трябва да го стартирате?
Има различни подходи, в зависимост от вашия бизнес работен процес, но като цяло трябва да се уверите, че всички тестове са „зелени“ (т.е. без грешки), преди да изпратите най-новите промени в кода в хранилището.
След това работите локално върху задачата си и когато смятате, че сте готови, изпълнете някои тестове, за да сте сигурни, че не сте счупили нещо. Не забравяйте, че вашият код може да причини грешки не само във вашата логика, но и неволно да наруши някое друго поведение в нечий друг код, написан отдавна.
Ако направим крачка напред, е възможно да се автоматизира много нещата. С различни инструменти за CI/CD можете да зададете тестове, които да се изпълняват всеки път, когато някой накара промени в конкретен клон на Git или преди сливане на код в производствения клон. Най-простият работен процес би бил да използвам Github Actions, имам отделно видео което го доказва.
Има различни мнения относно това колко голямо трябва да бъде така нареченото „тестово покритие“: опитайте всяка възможна операция и случай на всяка страница или ограничете работата до най-важните части.
Всъщност тук съм съгласен с хората, които обвиняват автоматизираното тестване, че отнема повече време, отколкото осигурява действителна полза. Това може да се случи, ако пишете тестове за всеки един детайл. Това каза, че може да се изисква от вашия проект: основният въпрос е „каква е цената на потенциалната грешка“.
С други думи, трябва да дадете приоритет на усилията си за тестване, като зададете въпроса „Какво ще се случи, ако този код се провали?“ Ако вашата платежна система има грешки, това ще се отрази пряко на бизнеса. Така че, ако функционалността на вашите роли/разрешения е повредена, това е огромен проблем със сигурността.
Харесва ми как Мат Стауфър го каза на конференция: „Първо трябва да тествате тези неща, които, ако се провалят, ще ви уволнят от работата ви.“ Разбира се, това е преувеличено, но разбирате идеята: опитайте първо важните неща. И след това други функции, ако имате време.
Всички горни примери са базирани на инструмента за предварително тестване на Laraveldefiвечер: PHPUnit . Но през годините в екосистемата се появиха други инструменти и един от най-новите популярни е ВРЕДИ . Създаден от официален служител на Laravel Нуно Мадуро , има за цел да опрости синтаксиса, правейки писането на код за тестове още по-бързо.
Под капака работи su PHPUnit, като допълнителен слой, просто се опитва да минимизира някои предварително повтарящи се частиdefiкрая на кода PHPUnit.
Нека разгледаме един пример. Запомнете класа за предварителен тест на функциятаdefiв Laravel? Ще ви напомня:
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);
}
}
Знаете ли как би изглеждал същият тест с PEST?
test('the application returns a successful response')->get('/')->assertStatus(200);
Да, ЕДИН ред код и това е всичко. И така, целта на PEST е да премахне режийните разходи за:
За да генерирате PEST тест в Laravel, трябва да посочите допълнителен флаг:
php artisan make:test HomepageTest --pest
Към момента на писане на тази статия PEST е доста популярен сред разработчиците на Laravel, но ваше лично предпочитание е дали да използвате този допълнителен инструмент и да научите неговия синтаксис, както и бележка за PHPUnit.
BlogInnovazione.it
Развитието на фини двигателни умения чрез оцветяване подготвя децата за по-сложни умения като писане. Оцветявам…
Военноморският сектор е истинска световна икономическа сила, която се е насочила към пазар от 150 милиарда...
Миналия понеделник Financial Times обяви сделка с OpenAI. FT лицензира своята журналистика от световна класа...
Милиони хора плащат за стрийминг услуги, като плащат месечни абонаментни такси. Разпространено е мнението, че вие…