عندما يتعلق الأمر بالاختبارات الآلية أو اختبارات الوحدة، في أي لغة برمجة، هناك رأيان متعارضان:
لذا، سنحاول في هذه المقالة إقناع الأول، خاصة من خلال توضيح مدى سهولة البدء في الاختبار الآلي في Laravel.
دعونا نتحدث أولاً عن "السبب"، ثم دعونا نرى بعض الأمثلة عن الكيفية.
تقوم الاختبارات الآلية بتشغيل أجزاء من التعليمات البرمجية والإبلاغ عن أي أخطاء. هذه هي أبسط طريقة لوصفهم. تخيل طرح ميزة جديدة في أحد التطبيقات، وبعد ذلك سيذهب مساعد الروبوت الشخصي ويختبر الميزة الجديدة يدويًا، بينما يختبر أيضًا ما إذا كان الكود الجديد لم يكسر أيًا من الميزات القديمة.
هذه هي الميزة الرئيسية: إعادة اختبار جميع الميزات تلقائيًا. قد يبدو هذا عملاً إضافيًا، ولكن إذا لم تطلب من "الروبوت" القيام بذلك، فيجب علينا بدلاً من ذلك القيام بذلك يدويًا، أليس كذلك؟
أو يمكن إصدار ميزات جديدة دون اختبار ما إذا كانت تعمل، على أمل أن يقوم المستخدمون بالإبلاغ عن الأخطاء.
يمكن أن تمنحنا الاختبارات الآلية العديد من المزايا:
حاول أن تتخيل تطبيقك بعد عام أو عامين، مع وجود مطورين جدد في الفريق لا يعرفون الكود المكتوب في السنوات السابقة، أو حتى كيفية اختباره.
لأداء الأول الاختبار الآلي في Laravel، لا تحتاج إلى كتابة أي رمز. نعم، لقد قرأت ذلك بشكل صحيح. تم تكوين كل شيء وإعداده بالفعل في التثبيت المسبقdefiليلة Laravel، بما في ذلك المثال الأساسي الأول.
يمكنك محاولة تثبيت مشروع Laravel وإجراء الاختبارات الأولى على الفور:
laravel new project
cd project
php artisan test
يجب أن تكون هذه هي النتيجة في وحدة التحكم الخاصة بك:
إذا ألقينا نظرة على ما قبلdefiليلة لارافيل /tests
، لدينا ملفين:
الاختبارات/الميزات/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()
يصبح نصًا قابلاً للقراءة عند عرض نتائج الاختبار، وذلك ببساطة عن طريق استبدال رمز التسطير بمسافة.
الاختبارات/الوحدة/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
يوجد اختباران فاشلان، تم وضع علامة "فشل" عليهما، مع التوضيحات أدناه وأسهم تشير إلى السطر الدقيق للاختبارات التي فشلت. تتم الإشارة إلى الأخطاء بهذه الطريقة.
لنفترض أن لدينا نموذجًا ونحتاج إلى اختبار حالات مختلفة: نتحقق من فشله باستخدام بيانات غير صالحة، ونتحقق من نجاحه باستخدام الإدخال الصحيح، وما إلى ذلك.
طقم البداية الرسمي بواسطة لارافيل بريز يشمل أنا اختبار الوظيفة داخله. دعونا نلقي نظرة على بعض الأمثلة من هناك:
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نيتا مع لارافل. على وجه التحديد، أنت بحاجة إلى هذا الجزء:
<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 بإنشاء ملف المصنع مسبقًاdefiنيتا مع بيانات خاطئة ل 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
الكود الذي تم إنشاؤه هو نفس اختبار الوحدة المسبقة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
}
في تجربتي الشخصية مع مشاريع Laravel، فإن الغالبية العظمى من الاختبارات هي اختبارات الميزات، وليست اختبارات الوحدة. أولاً، تحتاج إلى اختبار ما إذا كان تطبيقك يعمل، بالطريقة التي يستخدمه بها الأشخاص الحقيقيون.
بعد ذلك، إذا كان لديك حسابات خاصة أو منطق، يمكنك ذلك definire كوحدة، مع المعلمات، يمكنك إنشاء اختبارات وحدة خصيصًا لذلك.
في بعض الأحيان، تتطلب اختبارات الكتابة تعديل الكود نفسه وإعادة هيكلته لجعله أكثر "قابلية للاختبار": فصل الوحدات إلى فئات أو طرق خاصة.
ما هو الاستخدام الفعلي لهذا php artisan test
، متى يجب عليك تشغيله؟
هناك طرق مختلفة، اعتمادًا على سير عمل عملك، ولكن بشكل عام تحتاج إلى التأكد من أن جميع الاختبارات "خضراء" (أي خالية من الأخطاء) قبل دفع أحدث تغييرات التعليمات البرمجية إلى المستودع.
بعد ذلك، تعمل محليًا على مهمتك، وعندما تعتقد أنك انتهيت، قم بإجراء بعض الاختبارات للتأكد من أنك لم تكسر أي شيء. تذكر أن التعليمات البرمجية الخاصة بك قد تسبب أخطاء ليس فقط في المنطق الخاص بك ولكن أيضًا عن غير قصد في كسر بعض السلوكيات الأخرى في التعليمات البرمجية الخاصة بشخص آخر والتي تمت كتابتها منذ فترة طويلة.
إذا أخذنا خطوة أخرى إلى الأمام، فمن الممكن أن نقوم بالأتمتة كثير أشياء. باستخدام أدوات CI/CD المتنوعة، يمكنك تحديد الاختبارات التي سيتم تشغيلها عندما يقوم شخص ما بدفع التغييرات إلى فرع Git محدد أو قبل دمج التعليمات البرمجية في فرع الإنتاج. أبسط سير عمل هو استخدام Github Actions، لدي فيديو منفصل مما يثبت ذلك.
هناك آراء مختلفة حول الحجم الذي يجب أن يكون عليه ما يسمى بـ "تغطية الاختبار": جرب كل عملية وحالة ممكنة في كل صفحة، أو قصر العمل على الأجزاء الأكثر أهمية.
في الواقع، هذا هو المكان الذي أتفق فيه مع الأشخاص الذين يتهمون الاختبار الآلي بأنه يستغرق وقتًا أطول من تقديم فائدة فعلية. يمكن أن يحدث هذا إذا كتبت اختبارات لكل التفاصيل. ومع ذلك، قد يكون ذلك مطلوبًا لمشروعك: السؤال الرئيسي هو "ما هو ثمن الخطأ المحتمل".
بمعنى آخر، تحتاج إلى تحديد أولويات جهود الاختبار الخاصة بك عن طريق طرح السؤال "ماذا سيحدث إذا فشل هذا الرمز؟" إذا كان نظام الدفع الخاص بك يحتوي على أخطاء، فسيؤثر ذلك بشكل مباشر على عملك. لذا، إذا كانت وظيفة الأدوار/الأذونات الخاصة بك معطلة، فهذه مشكلة أمنية كبيرة.
تعجبني العبارة التي قالها مات ستوفر في أحد المؤتمرات: "عليك أولاً أن تختبر تلك الأشياء التي، إذا فشلت، ستؤدي إلى طردك من وظيفتك". بالطبع هذا مبالغة، لكنك فهمت الفكرة: جرب الأشياء المهمة أولاً. ثم ميزات أخرى، إذا كان لديك الوقت.
تعتمد جميع الأمثلة المذكورة أعلاه على أداة الاختبار المسبق Laraveldefiنيت: وحدة PHP . ولكن على مر السنين ظهرت أدوات أخرى في النظام البيئي، ومن بين أحدث الأدوات الشائعة PEST . تم إنشاؤها بواسطة موظف Laravel الرسمي نونو مادورو ، يهدف إلى تبسيط بناء الجملة، مما يجعل كتابة التعليمات البرمجية للاختبارات أسرع.
تحت غطاء محرك السيارة، فإنه يعمل su PHPUnit، كطبقة إضافية، تحاول فقط تقليل بعض الأجزاء المتكررة مسبقًاdefiنايت من كود 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 في Laravel، عليك تحديد علامة إضافية:
php artisan make:test HomepageTest --pest
حتى وقت كتابة هذه السطور، تحظى PEST بشعبية كبيرة بين مطوري Laravel، ولكن تفضيلك الشخصي هو ما إذا كنت تريد استخدام هذه الأداة الإضافية ومعرفة تركيبها، بالإضافة إلى ملاحظة PHPUnit.
BlogInnovazione.it
أعلنت صحيفة فاينانشيال تايمز يوم الاثنين الماضي عن صفقة مع OpenAI. "فاينانشيال تايمز" ترخص صحافتها ذات المستوى العالمي...
يدفع الملايين من الأشخاص مقابل خدمات البث، ويدفعون رسوم الاشتراك الشهرية. من الشائع أنك…
سوف تستمر شركة Coveware by Veeam في تقديم خدمات الاستجابة لحوادث الابتزاز السيبراني. ستوفر Coveware إمكانات الطب الشرعي والمعالجة...
تُحدث الصيانة التنبؤية ثورة في قطاع النفط والغاز، من خلال اتباع نهج مبتكر واستباقي لإدارة المحطات.