0Pricing
PHP Academy · درس

اختبار التكامل والاختبار الوظيفي

اختبر كيفية عمل المكوّنات معًا في الواقع

اختبار التكامل والاختبار الوظيفي درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.

ما بعد الوحدة

تثبت اختبارات الوحدات أن الفئة تعمل بمعزل عن غيرها، مع محاكاة كل ما تعتمد عليه. لكن المحاكيات قد تكون مضللة — إذ قد تتصرف قاعدة البيانات الفعلية أو موجّه إطار العمل أو طبقة HTTP بشكل مختلف. تتحقق اختبارات التكامل من عمل مكوّنين فعليين أو أكثر بعد ربطهما معًا؛ أما الاختبارات الوظيفية فتختبر التطبيق من البداية إلى النهاية عبر نقطة دخوله العامة، مثل طلب HTTP أو أمر وحدة التحكم. يوضح هذا الدرس موضع كل نوع وكيفية كتابته.

هرم الاختبارات

تتخذ مجموعة الاختبارات السليمة شكل هرم:

  • اختبارات وحدات كثيرة — سريعة ومعزولة وتُشغّل باستمرار.
  • اختبارات تكامل أقل — تستخدم قاعدة بيانات/قائمة انتظار/ذاكرة تخزين مؤقت فعلية، ولذلك هي أبطأ.
  • عدد قليل من الاختبارات الوظيفية/اختبارات E2E — تختبر المكدس كاملًا، وهي الأبطأ والأكثر عرضة للتعطل.

يؤدي عكس هذا الترتيب، أي إنشاء «مخروط آيس كريم» يتكون معظمه من اختبارات E2E، إلى مجموعة اختبارات بطيئة وغير مستقرة. ادفع بالتغطية إلى الأسفل كلما أمكن.

التكامل: اختبار المستودع مقابل قاعدة بيانات فعلية

يستخدم اختبار التكامل الخاص بالمستودع اتصالًا فعليًا بقاعدة البيانات — وليس محاكيًا — لاكتشاف أخطاء SQL والمخطط ومواءمة البيانات. استخدم قاعدة بيانات مخصصة للاختبارات، أو SQLite داخل الذاكرة إذا كانت مطابقة للهجتك بما يكفي، مع PDO فعلي.

<?php
use PHPUnit\Framework\TestCase;

final class UserRepositoryTest extends TestCase
{
    private \PDO $pdo;

    protected function setUp(): void
    {
        $this->pdo = new \PDO('sqlite::memory:');
        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        $this->pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)');
    }

    public function test_persists_and_finds_a_user(): void
    {
        $repo = new UserRepository($this->pdo);
        $id = $repo->create('ada@example.com');
        self::assertSame('ada@example.com', $repo->find($id)->email);
    }
}

العزل: التراجع عن المعاملة

يجب ألا تسرّب اختبارات التكامل حالتها إلى بعضها بعضًا. أسرع نمط هو تغليف كل اختبار داخل معاملة ثم التراجع عنها في tearDown() — فتعود قاعدة البيانات إلى حالة نظيفة من دون إعادة إدخال البيانات الأولية. (يُستخدم التفريغ كخيار احتياطي عندما تنفّذ الشيفرة قيد الاختبار عملية تأكيد للمعاملة أو تستخدم DDL.)

<?php
protected function setUp(): void
{
    $this->pdo = TestDb::connection();
    $this->pdo->beginTransaction();
}

protected function tearDown(): void
{
    $this->pdo->rollBack();   // undo everything this test did
}

بيانات الاختبار والمصانع

تحتاج الاختبارات إلى بيانات. تجنّب ملفات SQL الضخمة والهشة؛ وفضّل مصانع صغيرة تنشئ الصفوف التي يحتاج إليها الاختبار فقط، مع إعدادات افتراضية منطقية يمكنك تجاوزها في كل حالة. يحافظ ذلك على وضوح الغرض من كل اختبار ويجعله أكثر قدرة على التكيف مع نمو المخطط.

<?php
final class UserFactory
{
    public static function create(\PDO $pdo, array $overrides = []): int
    {
        $data = array_merge(['email' => 'user'.uniqid().'@test.dev'], $overrides);
        $stmt = $pdo->prepare('INSERT INTO users (email) VALUES (:email)');
        $stmt->execute($data);
        return (int) $pdo->lastInsertId();
    }
}

وظيفيًا: الوصول إلى التطبيق كعميل

يُشغّل الاختبار الوظيفي نواة التطبيق/الموجّه الفعليين باستخدام طلب يحاكي طلب العميل، ثم يتحقق من الاستجابة — رمز الحالة والترويسات والمحتوى. تنفّذ WebTestCase في Symfony ومساعدات اختبار HTTP في Laravel ذلك تحديدًا من دون تشغيل خادم ويب فعلي. فيما يلي البنية المستخدمة في Symfony.

<?php
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

final class HealthControllerTest extends WebTestCase
{
    public function test_health_endpoint_returns_ok(): void
    {
        $client = static::createClient();
        $client->request('GET', '/health');

        self::assertResponseIsSuccessful();        // 2xx
        self::assertJson($client->getResponse()->getContent());
    }
}

التحقق من الاستجابة

تتحقق الاختبارات الوظيفية من السلوك القابل للملاحظة عند الحد الفاصل: الحالة، وبنية JSON، والآثار الجانبية في قاعدة البيانات. في المثال أدناه، ينشئ طلب POST موردًا؛ ونتحقق من استجابة HTTP ومن وصول السجل فعلًا إلى التخزين، لإثبات تعاون المكدس بأكمله.

<?php
public function test_creating_a_user_persists_it(): void
{
    $client = static::createClient();
    $client->request('POST', '/users', server: [
        'CONTENT_TYPE' => 'application/json',
    ], content: json_encode(['email' => 'ada@example.com']));

    self::assertResponseStatusCodeSame(201);

    $repo = static::getContainer()->get(UserRepository::class);
    self::assertNotNull($repo->findByEmail('ada@example.com'));
}

محاكاة الحدود الخارجية انتقائيًا

حتى الاختبارات الوظيفية يجب ألا تستدعي مزوّدي الدفع الفعليين أو ترسل رسائل بريد إلكتروني حقيقية. استبدل الخدمات الخارجية الأبعد فقط — فاستبدلها في حاوية الاختبار ببدائل وهمية أو تطبيقات تعمل داخل الذاكرة — مع إبقاء شيفرتك وموجّهك وقاعدة بياناتك فعلية. يحافظ ذلك على دقة الاختبار من البداية إلى النهاية من دون استدعاءات شبكة غير مستقرة.

<?php
public function test_checkout_charges_via_gateway(): void
{
    $client = static::createClient();
    // Replace the real gateway binding with an in-memory fake:
    static::getContainer()->set(PaymentGateway::class, new FakePaymentGateway());

    $client->request('POST', '/checkout', content: json_encode(['cart' => 1]));
    self::assertResponseIsSuccessful();
}

مجموعات منفصلة، وسرعات منفصلة

قسّم ملف phpunit.xml إلى مجموعات اختبارات، بحيث تُشغّل مجموعة اختبارات الوحدات السريعة مع كل عملية حفظ، بينما تُشغّل مجموعتا اختبارات التكامل والاختبارات الوظيفية عند الطلب أو في CI. جمّع الاختبارات حسب المجلد وحدد المجموعة باستخدام --testsuite.

<phpunit>
  <testsuites>
    <testsuite name="unit">
      <directory>tests/Unit</directory>
    </testsuite>
    <testsuite name="integration">
      <directory>tests/Integration</directory>
    </testsuite>
    <testsuite name="functional">
      <directory>tests/Functional</directory>
    </testsuite>
  </testsuites>
</phpunit>

الحتمية أهم من التغطية

أسرع طريقة لفقدان الثقة في مجموعة الاختبارات هي عدم استقرارها. اجعل اختبارات التكامل والاختبارات الوظيفية حتمية:

  • جمّد الوقت، مثلًا عبر حقن ساعة، بدلًا من استدعاء time().
  • ثبّت العشوائية، وتجنّب التأكيدات المعتمدة على الترتيب في الاستعلامات غير المرتبة.
  • أعد ضبط حالة قاعدة البيانات في كل اختبار، باستخدام التراجع عن المعاملة أو التفريغ.
  • لا تعتمد أبدًا على ترتيب تنفيذ الاختبارات.

طابق قاعدة بيانات الاختبار مع الإنتاج

تتميز SQLite داخل الذاكرة بالسرعة، لكن اختلافات اللهجة، مثل حساسية حالة الأحرف ودوال JSON وفرض المفاتيح الخارجية والأنواع، قد تخفي أخطاء لا تظهر إلا على المحرك الفعلي. بالنسبة إلى الشيفرة التي تستخدم SQL خاصًا بالمحرك، شغّل اختبارات التكامل على المحرك نفسه المستخدم في الإنتاج — وغالبًا داخل حاوية Docker مؤقتة في CI — بدلًا من استخدام بديل ملائم فحسب.

<?php
// Read connection from environment so CI points at a real Postgres/MySQL container
$dsn = getenv('TEST_DATABASE_URL') ?: 'sqlite::memory:';
$pdo = new \PDO($dsn);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// In CI, TEST_DATABASE_URL targets the same engine as production.

فحص سريع

كيف ينبغي للاختبار الوظيفي أن يتعامل مع مزوّد دفع تابع لجهة خارجية؟

مراجعة

تعلّمت اختبار المكوّنات معًا:

  • تستخدم اختبارات التكامل قواعد بيانات وخدمات فعلية لاكتشاف ما تخفيه المحاكيات؛ وتعزلها عبر التراجع عن المعاملة.
  • تشغّل الاختبارات الوظيفية النواة الفعلية باستخدام طلبات تحاكي طلبات العملاء، وتتحقق من الاستجابات والآثار الجانبية.
  • تنشئ المصانع الحد الأدنى من البيانات؛ ولا تُحاكى إلا الحدود الخارجية الأبعد للخدمات الخارجية.
  • قسّم مجموعات الاختبارات حسب السرعة، وحافظ على حتمية الاختبارات الأبطأ.

التالي: قياس جودة الاختبارات باستخدام اختبار الطفرات.

الأسئلة الشائعة

هل درس «اختبار التكامل والاختبار الوظيفي» مجاني؟

نعم — نص درس «اختبار التكامل والاختبار الوظيفي» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.

ماذا ستتعلم في «اختبار التكامل والاختبار الوظيفي»؟

اختبر كيفية عمل المكوّنات معًا في الواقع تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟

لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «اختبار التكامل والاختبار الوظيفي»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟

نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. سير عمل التطوير المدفوع بالاختبارات
  2. المحاكاة والبدائل باستخدام Mockery
  3. اختبار التكامل والاختبار الوظيفي
  4. اختبار الطفرات باستخدام Infection
← العودة إلى PHP Academy