الاختبارات و TDD
التطوير الموجه بالسلوك (BDD)
التطوير الموجه بالسلوك (BDD)
يسد التطوير الموجه بالسلوك (BDD) الفجوة بين أعضاء الفريق التقنيين وغير التقنيين باستخدام اللغة الطبيعية لوصف سلوك التطبيق. يركز BDD على سلوك التطبيق من منظور المستخدم.
فهم BDD
فوائد BDD:
- تعاون أفضل بين المطورين، ضمان الجودة، وأصحاب المصلحة
- توثيق حي لسلوك النظام
- متطلبات ومعايير قبول أوضح
- اختبارات مكتوبة بلغة الأعمال
- التركيز على قيمة المستخدم
بناء جملة Gherkin
Gherkin هي اللغة المستخدمة لكتابة سيناريوهات BDD. تستخدم تنسيق Given-When-Then:
# features/user_authentication.feature
Feature: User Authentication
As a user
I want to login to the system
So that I can access my account
Scenario: Successful login with valid credentials
Given I am on the login page
When I enter "user@example.com" as email
And I enter "password123" as password
And I click the "Login" button
Then I should see "Welcome back"
And I should be on the dashboard page
Scenario: Failed login with invalid credentials
Given I am on the login page
When I enter "wrong@example.com" as email
And I enter "wrongpass" as password
And I click the "Login" button
Then I should see "Invalid credentials"
And I should remain on the login page
Scenario Outline: Login validation
Given I am on the login page
When I enter "" as email
And I enter "" as password
And I click the "Login" button
Then I should see ""
Examples:
| email | password | message |
| valid@example.com | password123 | Welcome back |
| invalid@test.com | wrong | Invalid credentials |
| user@example.com | | Password is required |
| | password123 | Email is required |
Behat لـ PHP
Behat هو إطار عمل BDD الأكثر شعبية لـ PHP:
// تثبيت Behat
composer require --dev behat/behat
vendor/bin/behat --init
// features/bootstrap/FeatureContext.php
use Behat\Behat\Context\Context;
use Behat\Behat\Tester\Exception\PendingException;
use PHPUnit\Framework\Assert;
class FeatureContext implements Context
{
private $response;
private $email;
private $password;
/**
* @Given I am on the login page
*/
public function iAmOnTheLoginPage()
{
$this->response = $this->get('/login');
Assert::assertEquals(200, $this->response->getStatusCode());
}
/**
* @When I enter :arg1 as email
*/
public function iEnterAsEmail($email)
{
$this->email = $email;
}
/**
* @When I enter :arg1 as password
*/
public function iEnterAsPassword($password)
{
$this->password = $password;
}
/**
* @When I click the :arg1 button
*/
public function iClickTheButton($button)
{
$this->response = $this->post('/login', [
'email' => $this->email,
'password' => $this->password
]);
}
/**
* @Then I should see :arg1
*/
public function iShouldSee($text)
{
Assert::assertStringContainsString(
$text,
$this->response->getContent()
);
}
/**
* @Then I should be on the dashboard page
*/
public function iShouldBeOnTheDashboardPage()
{
Assert::assertEquals('/dashboard', $this->response->headers->get('Location'));
}
}
تكامل Laravel و Behat
// تثبيت امتداد Laravel Behat
composer require --dev laracasts/behat-laravel-extension
// behat.yml
default:
extensions:
Laracasts\Behat:
env_path: .env.behat
suites:
default:
contexts:
- FeatureContext
- Laracasts\Behat\Context\DatabaseTransactions
- Laracasts\Behat\Context\Services\FilesystemManager
سيناريوهات BDD المعقدة
# features/shopping_cart.feature
Feature: Shopping Cart Management
As a customer
I want to manage items in my cart
So that I can purchase products
Background:
Given the following products exist:
| name | price | stock |
| Laptop | 999 | 10 |
| Mouse | 25 | 50 |
| Keyboard | 75 | 30 |
And I am logged in as "customer@example.com"
Scenario: Add product to cart
Given I am on the product page for "Laptop"
When I click "Add to Cart"
Then I should see "Product added to cart"
And my cart should contain 1 item
And the cart total should be "$999.00"
Scenario: Update product quantity in cart
Given I have the following items in my cart:
| product | quantity |
| Laptop | 1 |
| Mouse | 2 |
When I am on the cart page
And I change the quantity of "Mouse" to 5
And I click "Update Cart"
Then the cart should contain:
| product | quantity | subtotal |
| Laptop | 1 | $999.00 |
| Mouse | 5 | $125.00 |
And the cart total should be "$1,124.00"
Scenario: Remove product from cart
Given I have "Laptop" in my cart
When I am on the cart page
And I click "Remove" for "Laptop"
Then my cart should be empty
And I should see "Your cart is empty"
أفضل ممارسات BDD:
- كتابة السيناريوهات قبل التنفيذ (من الخارج إلى الداخل)
- استخدام لغة الأعمال، وليس المصطلحات التقنية
- التركيز على السلوك، وليس التنفيذ
- إبقاء السيناريوهات مستقلة
- استخدام Background للإعداد المشترك
- إعادة استخدام تعريفات الخطوات عبر الميزات
العلامات لتنظيم الاختبارات
# استخدام العلامات لتنظيم وتصفية الاختبارات
@smoke @critical
Feature: User Registration
@javascript @slow
Scenario: Register with email verification
# Steps here
@wip
Scenario: Social media registration
# Steps here
# تشغيل اختبارات محددة بعلامات
vendor/bin/behat --tags=smoke
vendor/bin/behat --tags=~@slow # استبعاد الاختبارات البطيئة
vendor/bin/behat --tags="@smoke&&@critical"
أخطاء BDD الشائعة:
- كتابة سيناريوهات تقنية للغاية
- اختبار التنفيذ بدلاً من السلوك
- إنشاء سيناريوهات تابعة
- الإفراط في استخدام Background (يجعل السيناريوهات غير واضحة)
- عدم إشراك أصحاب المصلحة في كتابة السيناريوهات
تمرين:
- اكتب ملف ميزة لتدفق تسجيل المستخدم
- قم بتضمين سيناريوهات لـ: التسجيل الناجح، البريد الإلكتروني المكرر، البيانات غير الصالحة
- نفذ تعريفات الخطوات باستخدام Behat
- أضف مخطط سيناريو للتحقق من صحة كلمة المرور
- استخدم العلامات لوضع علامة على السيناريوهات الحرجة
BDD مقابل TDD
الاختلافات الرئيسية:
- TDD: يركز على اختبارات الوحدة، مدفوع بالتنفيذ، لغة تقنية
- BDD: يركز على السلوك، مدفوع بالأعمال، لغة طبيعية
- كلاهما: يمكن استخدامهما معًا - BDD للميزات، TDD للتنفيذ
ينشئ BDD مواصفات قابلة للتنفيذ تعمل كتوثيق واختبارات آلية، مما يحسن التواصل ويقلل من سوء الفهم بين أعضاء الفريق.