ما هو BDD ولماذا ليس مجرد موضة عابرة؟
\n\nهل سبق لك أن كتبت كودًا يعمل بشكل مثالي من وجهة نظر المترجم، لكنه لا يفعل ما يريده العملاء على الإطلاق؟ هل تعرف شعور المدير الذي ينظر إلى الميزة المكتملة ويقول: "كنت أتوقع شيئًا مختلفًا"؟ إذا كان الأمر مألوفًا، فمرحبًا بك في عالم BDD (Behaviour-Driven Development) — التطوير القائم على السلوك. إنها ليست مجرد منهجية، بل جسر بين "أريد أن يكون الزر أخضر" و"هذا اختبار يفشل إذا لم يكن الزر أخضر".
\n\nعلى عكس TDD التقليدي (Test-Driven Development)، حيث نفكر في الاختبارات كوسيلة للتحقق من الوظائف، يركز BDD على سلوك النظام من وجهة نظر المستخدم. في سياق C++، هذا مهم بشكل خاص: اللغة معقدة، والمشاريع ضخمة، وإعادة كتابة المنطق بعد سوء الفهم مع العميل مكلفة للغاية. يساعدك BDD في C++ على قتل عصفورين بحجر واحد: الحصول على وثائق يفهمها حتى مدير المشروع، ومجموعة من الاختبارات الآلية التي تمنع كسر منطق الأعمال.
\n\nفي هذه المقالة، سنشرح كيفية تطبيق التطوير القائم على السلوك في مشروع C++ حقيقي. بدون حشو، مع كود وأساليب عملية.
\n\nأساسيات BDD: لغة Gherkin ودورها في C++
\n\nقلب BDD هو السيناريوهات المكتوبة بلغة Gherkin. تبدو كالتالي:
\n\nFeature: تحويل الأموال بين الحسابات\n Scenario: تحويل ناجح عند وجود رصيد كافٍ\n Given لدى المستخدم حساب A برصيد 1000 ريال\n And لدى المستخدم حساب B برصيد 0 ريال\n When يقوم المستخدم بتحويل 500 ريال من الحساب A إلى الحساب B\n Then يصبح رصيد الحساب A 500 ريال\n And يصبح رصيد الحساب B 500 ريال\n\nنعم، هذا ليس كود C++. ولكن هذا هو السحر: السيناريو يُكتب بلغة بشرية، ثم يُربط بكود C++ الحقيقي عبر خطوات (steps). كل سطر Given أو When أو Then يتحول إلى دالة تستدعي كلاساتك وتتحقق من النتائج.
بالنسبة لـ C++، هناك عدة أطر عمل تدعم Gherkin. أشهرها:
\n\n- \n
- Catch2 بأسلوب BDD — خفيف، مدمج في Catch2، ولا يتطلب محللات خارجية. \n
- Cucumber-CPP — تطبيق كامل لـ Cucumber في C++، يحلل ملفات .feature مباشرة. \n
- Boost.Test — يدعم أيضًا بناء جملة مشابهًا لـ BDD، لكنه أقل مرونة. \n
أنصح شخصيًا بالبدء مع Catch2: فهو موجود بالفعل في 90% من مشاريع C++، ولا تحتاج لتثبيت أي شيء إضافي لاستخدام BDD. إذا كان المشروع كبيرًا وتحتاج إلى تكامل مع محللي الأعمال، فاستخدم Cucumber-CPP.
\n\nBDD مع Catch2: كتابة السيناريوهات مباشرة في الكود
\n\nيوفر Catch2 وحدات ماكرو مثل SCENARIO وGIVEN وWHEN وTHEN وAND_WHEN/AND_THEN. هذا ليس Gherkin نقيًا، لكن الأسلوب قريب جدًا. دعنا نكتب اختبارًا لحساب بنكي بسيط.
أولاً، كلاس الحساب:
\n\n// account.h\n#pragma once\n#include <string>\n#include <stdexcept>\n\nclass Account {\npublic:\n explicit Account(std::string owner, double initial_balance = 0.0)\n : owner_(std::move(owner)), balance_(initial_balance) {}\n\n void deposit(double amount) {\n if (amount <= 0) throw std::invalid_argument("Amount must be positive");\n balance_ += amount;\n }\n\n void withdraw(double amount) {\n if (amount > balance_) throw std::runtime_error("Insufficient funds");\n if (amount <= 0) throw std::invalid_argument("Amount must be positive");\n balance_ -= amount;\n }\n\n double balance() const { return balance_; }\n const std::string& owner() const { return owner_; }\n\nprivate:\n std::string owner_;\n double balance_ = 0.0;\n};\n\nوالآن، اختبار BDD باستخدام Catch2:
\n\n// tes