وقتی صحبت از تست های خودکار یا تست های واحد می شود، در هر زبان برنامه نویسی، دو نظر مخالف وجود دارد:
بنابراین، با این مقاله سعی خواهیم کرد که اولی را متقاعد کنیم، به خصوص با نشان دادن اینکه چقدر آسان است که با تست خودکار در لاراول شروع کنید.
ابتدا بیایید در مورد "چرا" صحبت کنیم و سپس چند نمونه از چگونگی آن را ببینیم.
تستهای خودکار بخشهایی از کد را اجرا میکنند و هر گونه خطا را گزارش میکنند. این ساده ترین راه برای توصیف آنهاست. تصور کنید که یک ویژگی جدید را در یک برنامه اجرا کنید، و سپس یک دستیار ربات شخصی میرود و به صورت دستی ویژگی جدید را آزمایش میکند و همچنین آزمایش میکند که آیا کد جدید هیچ یک از ویژگیهای قدیمی را خراب نمیکند یا خیر.
این مزیت اصلی است: آزمایش مجدد همه ویژگی ها به صورت خودکار. ممکن است این کار اضافی به نظر برسد، اما اگر به «ربات» نگویید که این کار را انجام دهد، ما باید آن را به صورت دستی انجام دهیم، درست است؟
یا میتوان ویژگیهای جدید را بدون آزمایش عملکرد آنها منتشر کرد، به این امید که کاربران اشکالات را گزارش کنند.
تستهای خودکار میتوانند چندین مزیت به ما بدهند:
سعی کنید برنامه خود را در یک یا دو سال با توسعه دهندگان جدیدی در تیم تصور کنید که کدهای نوشته شده در سال های گذشته را نمی دانند یا حتی نحوه آزمایش آن را نمی دانند.
برای اجرای اول تست خودکار در لاراول، نیازی به نوشتن هیچ کدی ندارید. بله شما آن را درست خواندید. همه چیز قبلاً در پیش نصب پیکربندی و آماده شده استdefinite of Laravel، از جمله اولین نمونه اولیه.
می توانید یک پروژه لاراول را نصب کنید و بلافاصله اولین تست ها را اجرا کنید:
laravel new project
cd project
php artisan test
این باید نتیجه در کنسول شما باشد:
اگر نگاهی به پیش بیندازیمdefiنیت لاراول /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 را گسترش می دهد PHP واحداز نظر ساختاری، این تنها چیزی است که باید بدانید، هر چیز دیگری بستگی به چیزهایی دارد که میخواهید آزمایش کنید.
برای ایجاد یک کلاس آزمایشی خالی، به سادگی این دستور را اجرا کنید:
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
دو تست ناموفق وجود دارد که با عنوان FAIL مشخص شده اند، با توضیحات زیر و فلش هایی که به خط دقیق تست هایی که شکست خورده اند اشاره می کند. خطاها از این طریق نشان داده می شوند.
فرض کنید فرمی داریم و باید موارد مختلفی را آزمایش کنیم: بررسی می کنیم که آیا با داده های نامعتبر مشکل دارد یا خیر، بررسی می کنیم که آیا با ورودی صحیح موفق شده است یا خیر و غیره.
کیت شروع رسمی توسط لاراول بریز شامل 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()
. شما می توانید تمام ادعاهای موجود در اسناد رسمی را بررسی کنید PHP واحد e پاسخ لاراول . توجه داشته باشید که برخی ادعاهای کلی در مورد این موضوع وجود دارد $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',
]);
حال بیایید نمونه دیگری از صفحه ورود با لاراول بریز را ببینیم
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();
}
}
این در مورد فرم ورود است. منطق مشابه ثبت نام است، درست است؟ اما سه روش به جای دو، بنابراین این نمونه ای از آزمایش سناریوهای خوب و بد است. بنابراین، منطق مشترک این است که شما باید هر دو مورد را آزمایش کنید: چه زمانی کارها خوب پیش می روند و چه زمانی که شکست می خورند.
همچنین آنچه در این تست می بینید استفاده از کارخانه های پایگاه داده : لاراول کاربر جعلی ایجاد می کند ( دوباره، در پایگاه داده آزمایشی به روز شده شما ) و سپس سعی می کند با اعتبار صحیح یا نادرست وارد سیستم شود.
یک بار دیگر، لاراول پیش کارخانه را تولید می کندdefinita با داده های نادرست برای 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),
];
}
}
ببینید، چه تعداد چیز توسط خود لاراول آماده شده است، پس آیا شروع آزمایش برای ما آسان است؟
پس اگر اجرا کنیم php artisan test
پس از نصب لاراول بریز، باید چیزی شبیه به این را ببینیم:
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
?.
چه تفاوتی بین آنها وجود دارد؟
در سطح جهانی، خارج از اکوسیستم لاراول/PHP، چندین نوع تست خودکار وجود دارد. می توانید اصطلاحاتی مانند:
پیچیده به نظر می رسد و تفاوت های واقعی بین این نوع تست ها گاهی مبهم است. به همین دلیل است که لاراول تمام این اصطلاحات گیج کننده را ساده کرده و آنها را به دو دسته تقسیم کرده است: واحد/ویژگی.
به زبان ساده، تستهای ویژگی سعی میکنند عملکرد واقعی برنامههای شما را اجرا کنند: URL را دریافت کنید، با API تماس بگیرید، رفتار دقیقی مانند پر کردن فرم را تقلید کنید. تست های ویژگی معمولاً عملیات مشابه یا مشابهی را انجام می دهند که هر کاربر پروژه به صورت دستی در زندگی واقعی انجام می دهد.
آزمون های واحد دو معنی دارند. به طور کلی، ممکن است متوجه شوید که هر آزمایش خودکار «تست واحد» نامیده می شود و کل فرآیند را می توان «تست واحد» نامید. اما در زمینه عملکرد در مقابل واحد، این فرآیند در مورد آزمایش یک واحد کد غیرعمومی خاص، به صورت مجزا است. به عنوان مثال، شما یک کلاس لاراول با روشی دارید که چیزی را محاسبه می کند، مانند قیمت کل سفارش با پارامترها. بنابراین، آزمون واحد بیان می کند که آیا نتایج صحیح از آن روش (واحد کد)، با پارامترهای مختلف برگردانده می شود یا خیر.
برای ایجاد یک تست واحد، باید یک پرچم اضافه کنید:
php artisan make:test OrderPriceTest --unit
کد تولید شده همانند آزمون پیش واحد استdefiسیستم لاراول:
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
}
در تجربه شخصی من با پروژه های لاراول، اکثریت قریب به اتفاق تست ها تست های ویژگی هستند، نه تست های واحد. ابتدا، باید آزمایش کنید که آیا برنامه شما کار می کند یا خیر، روشی که افراد واقعی از آن استفاده می کنند.
بعد، اگر محاسبات یا منطق خاصی دارید، می توانید definire به عنوان یک واحد، با پارامترها، می توانید تست های واحد را به طور خاص برای آن ایجاد کنید.
گاهی اوقات، نوشتن تستها مستلزم اصلاح خود کد و اصلاح مجدد آن برای «آزمایشپذیر»تر شدن آن است: جداسازی واحدها به کلاسها یا متدهای خاص.
استفاده واقعی از این چیست php artisan test
، چه زمانی باید آن را اجرا کنید؟
بسته به گردش کار کسب و کار شما، رویکردهای متفاوتی وجود دارد، اما به طور کلی باید اطمینان حاصل کنید که همه آزمایشها قبل از فشار دادن تغییرات کد نهایی به مخزن «سبز» (یعنی بدون خطا) هستند.
سپس، به صورت محلی روی کار خود کار میکنید، و وقتی فکر میکنید کارتان تمام شده است، چند آزمایش انجام دهید تا مطمئن شوید چیزی را خراب نکردهاید. به یاد داشته باشید، کد شما ممکن است نه تنها در منطق شما اشکالاتی ایجاد کند، بلکه ممکن است ناخواسته برخی از رفتارهای دیگر را در کد شخص دیگری که مدت ها پیش نوشته شده است، بشکند.
اگر یک قدم جلوتر برویم، امکان خودکارسازی وجود دارد پوست اندازی چیزها با ابزارهای مختلف CI/CD، میتوانید تستهایی را مشخص کنید که هر زمان که کسی تغییراتی را در یک شاخه Git خاص یا قبل از ادغام کد در شاخه تولید اعمال کند، اجرا شود. من دارم ساده ترین گردش کار استفاده از Github Actions است یک ویدیوی جداگانه که آن را ثابت می کند.
نظرات مختلفی در مورد اینکه به اصطلاح "پوشش تست" چقدر باید باشد وجود دارد: هر عملیات و مورد ممکن را در هر صفحه امتحان کنید یا کار را به مهمترین قسمت ها محدود کنید.
در واقع، اینجا جایی است که من با افرادی موافقم که تست خودکار را متهم به صرف زمان بیشتر از ارائه سود واقعی می کنند. این می تواند اتفاق بیفتد اگر برای هر جزئیات تست بنویسید. گفته می شود، ممکن است پروژه شما مورد نیاز باشد: سوال اصلی این است که "قیمت خطای احتمالی چقدر است".
به عبارت دیگر، باید تلاشهای آزمایشی خود را با پرسیدن این سوال اولویتبندی کنید که «اگر این کد شکست بخورد چه اتفاقی میافتد؟» اگر سیستم پرداخت شما دارای اشکال باشد، مستقیماً بر تجارت تأثیر می گذارد. بنابراین اگر عملکرد نقشها/مجوزهای شما خراب است، این یک مشکل امنیتی بزرگ است.
من دوست دارم که مت استافر آن را در یک کنفرانس بیان کرد: "شما باید ابتدا چیزهایی را آزمایش کنید که اگر شکست بخورند، شما را از کارتان اخراج می کنند." البته این اغراق آمیز است، اما شما این ایده را دریافت می کنید: ابتدا چیزهای مهم را امتحان کنید. و سپس ویژگی های دیگر، اگر وقت دارید.
تمام مثال های بالا بر اساس ابزار پیش تست لاراول هستندdefiنیت: PHP واحد . اما در طول سال ها ابزارهای دیگری در اکوسیستم ظاهر شده اند و یکی از جدیدترین ابزارهای محبوب است آفات . ایجاد شده توسط کارمند رسمی لاراول نونو مادورو ، با هدف ساده سازی نحو، نوشتن کد برای تست ها را سریعتر می کند.
زیر کاپوت، اجرا می شود su PHPUnit، به عنوان یک لایه اضافی، فقط سعی می کند برخی از قسمت های از پیش تکرار شده را به حداقل برساندdefinite کد PHPUnit.
بیایید به یک مثال نگاه کنیم. کلاس تست پیش ویژگی را به خاطر بسپاریدdefiنیت شده در لاراول؟ من به شما یادآوری میکنم:
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 در لاراول، باید یک پرچم اضافی مشخص کنید:
php artisan make:test HomepageTest --pest
از زمان نگارش این مقاله، PEST در بین توسعه دهندگان لاراول بسیار محبوب است، اما ترجیح شخصی شماست که از این ابزار اضافی استفاده کنید و نحو آن و همچنین یادداشت PHPUnit را یاد بگیرید.
BlogInnovazione.it
دوشنبه گذشته، فایننشال تایمز از قراردادی با OpenAI خبر داد. FT مجوز روزنامه نگاری در سطح جهانی خود را صادر می کند…
میلیونها نفر برای خدمات استریم پرداخت میکنند و هزینه اشتراک ماهانه میپردازند. این عقیده رایج است که شما…
Coveware توسط Veeam به ارائه خدمات پاسخگویی به حوادث اخاذی سایبری ادامه خواهد داد. Coveware قابلیتهای پزشکی قانونی و اصلاحی را ارائه میدهد…
تعمیر و نگهداری پیش بینی شده با رویکردی نوآورانه و پیشگیرانه برای مدیریت کارخانه، بخش نفت و گاز را متحول می کند.…