اختبار C++: نظرة عامة على الأطر والأدوات (Google Test, Catch2, doctest)

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

اختبار C++: الأطر والأدوات



الاختبار عالي الجودة هو جزء لا يتجزأ من التطوير بلغة C++. على عكس اللغات الديناميكية، تتطلب C++ اهتمامًا خاصًا بإدارة الذاكرة، وعمر الكائنات، والأداء. في هذه المقالة، سنحلل ثلاثة من أشهر أطر اختبار الوحدات: Google Test، Catch2 و doctest، بالإضافة إلى التطرق للأدوات الإضافية.



لماذا نحتاج إلى الاختبار في C++؟

C++ هي لغة ذات إدارة يدوية للذاكرة وسحر قوي للقوالب (templates). بدون اختبارات، من السهل الوقوع في:

  • تسريبات الذاكرة والتحرير المزدوج
  • سلوك غير محدد (UB) عند العمل مع المؤشرات
  • أخطاء في تحميل المعاملات (operator overloading) ومنشئات النسخ
  • انتكاسات (regressions) عند إعادة هيكلة الكود القديم (legacy code)

تعمل أطر الاختبار على أتمتة التحقق وتساعد في العثور على المشكلات بسرعة.



Google Test (gtest)

الإطار الأكثر شيوعًا من Google. يدعم جميع الوظائف الأساسية: وحدات الماكرو ASSERT_* و EXPECT_*، الاختبارات المعلمية (parameterized tests)، المثبتات (test fixtures)، اختبارات الموت (death tests).



التثبيت (عبر CMake FetchContent)

include(FetchContent)FetchContent_Declare(  googletest  GIT_REPOSITORY https://github.com/google/googletest.git  GIT_TAG release-1.14.0)FetchContent_MakeAvailable(googletest)


مثال لاختبار بسيط

#include <gtest/gtest.h>

int factorial(int n) { return (n <= 1) ? 1 : n * factorial(n - 1);}

TEST(FactorialTest, HandlesZeroInput) { EXPECT_EQ(factorial(0), 1);}

TEST(FactorialTest, HandlesPositiveInput) { EXPECT_EQ(factorial(5), 120); EXPECT_EQ(factorial(3), 6);}

int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();}


المثبتات والاختبارات المعلمية

class MathFixture : public ::testing::Test {protected:    void SetUp() override { /* تهيئة */ }    void TearDown() override { /* تنظيف */ }};

// اختبار معلميclass SumTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};TEST_P(SumTest, CheckSum) { auto [a, b, expected] = GetParam(); EXPECT_EQ(a + b, expected);}INSTANTIATE_TEST_SUITE_P(SumParams, SumTest, ::testing::Values( std::make_tuple(1, 1, 2), std::make_tuple(0, 0, 0) ));


الإيجابيات: مجتمع ضخم، تكامل مع CI/CD، وحدات ماكرو قوية، دعم اختبارات الموت.
السلبيات: ترجمة بطيئة نسبيًا، حجم ملف تنفيذي كبير.



Catch2

إطار حديث يركز على سهولة قراءة الاختبارات. يستخدم أسلوب BDD (التطوير القائم على السلوك) ويدعم الأقسام (sections).



التثبيت (header-only)

Catch2 يتم توزيعه كملف رأسي واحد. يكفي تنزيل catch_amalgamated.hpp وتضمينه.



مثال مع الأقسام

#define CATCH_CONFIG_MAIN#include <catch2/catch.hpp>

int divide(int a, int b) { if (b == 0) throw std::invalid_argument("القسمة على صفر"); return a / b;}

TEST_CASE("اختبارات القسمة", "[math]") { SECTION("قسمة عادية") { REQUIRE(divide(10, 2) == 5); } SECTION("القسمة على صفر") { REQUIRE_THROWS_AS(divide(5, 0), std::invalid_argument); }}


أسلوب BDD (Given-When-Then)

SCENARIO("عملية push_back في المتجه", "[container]") {    GIVEN("متجه فارغ") {        std::vector<int> v;        WHEN("نقوم بدفع عنصر واحد") {            v.push_back(42);            THEN("يصبح الحجم 1") {                REQUIRE(v.size() == 1);            }            AND_THEN("العنصر صحيح") {                REQUIRE(v[0] == 42);                    

توصيات