اختبار في C++: مراجعة الأطر والأدوات
الاختبار جزء لا يتجزأ من التطوير الاحترافي بلغة C++. على عكس اللغات ذات الكتابة الديناميكية، تتطلب C++ اهتمامًا خاصًا بإدارة الذاكرة، RAII، والبرامج الوصفية القالبية. بدون اختبارات عالية الجودة، حتى إعادة الهيكلة البسيطة يمكن أن تؤدي إلى أخطاء يصعب اكتشافها. في هذه المقالة، سنستعرض الأطر الرئيسية لاختبار الوحدات في C++، وميزاتها، وأمثلة على استخدامها.
لماذا اختبار كود C++؟
الأسباب الرئيسية لاعتماد الاختبار:
- أمان إعادة الهيكلة — تثبت الاختبارات السلوك الصحيح قبل التغييرات.
- التوثيق — تظهر الاختبارات كيفية استخدام API.
- اكتشاف الانحدارات — فحص تلقائي بعد كل commit.
- التحقق من الحالات الحدودية — التعامل مع nullptr، الحاويات الفارغة، التجاوز.
الأطر الشائعة لـ C++
1. Google Test (gtest)
الإطار الأكثر انتشارًا من Google. يدعم التسجيل التلقائي للاختبارات، وحدات الماكرو ASSERT_* و EXPECT_*، والاختبارات المعلمية، والـ mocks عبر Google Mock.
#include <gtest/gtest.h>
int add(int a, int b) { return a + b; }
TEST(AdditionTest, PositiveNumbers) { EXPECT_EQ(add(2, 3), 5); EXPECT_EQ(add(0, 0), 0);}
TEST(AdditionTest, NegativeNumbers) { EXPECT_EQ(add(-1, -2), -3);}
int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();}الميزات:
- دعم اختبارات الموت (death tests) للتحقق من assert.
- مصنع الـ mocks (gmock) لعزل التبعيات.
- التكامل مع CMake (find_package(GTest)).
2. Catch2
إطار حديث مع بناء جملة تعبيري. لا يتطلب تسجيلًا منفصلًا للاختبارات — يكفي ماكرو TEST_CASE. يدعم نمط BDD (Given-When-Then).
#include <catch2/catch_test_macros.hpp>
int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1);}
TEST_CASE("Factorials are computed", "[math]") { REQUIRE(factorial(0) == 1); REQUIRE(factorial(1) == 1); REQUIRE(factorial(5) == 120);}
TEST_CASE("Negative input", "[math][edge]") { REQUIRE_THROWS_AS(factorial(-1), std::domain_error);}الميزات:
- توليد بيانات الاختبار (GENERATE).
- تقسيم الاختبارات إلى أقسام (SECTION).
- اختيار main() تلقائيًا (إذا تم تعريف CATCH_CONFIG_MAIN).
3. doctest
إطار خفيف وسريع، مع وقت ترجمة ضئيل. مثالي للمشاريع التي تهم فيها سرعة البناء. بناء الجملة مشابه لـ Catch2.
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN#include <doctest/doctest.h>
int divide(int a, int b) { if (b == 0) throw std::invalid_argument("division by zero"); return a / b;}
TEST_CASE("Division") { CHECK(divide(10, 2) == 5); CHECK_THROWS_AS(divide(5, 0), std::invalid_argument);}الميزات:
- ترجمة سريعة جدًا (ملف رأسي بحجم ~5 كيلوبايت).
- دعم وحدات الماكرو CHECK، REQUIRE، WARN.
- مُبلّغ مدمج بتنسيق XML/JSON.
4. Boost.Test
جزء من مكتبات Boost. يتمتع بإمكانيات غنية، لكنه يتطلب كودًا تمهيديًا أكثر. يدعم التسجيل التلقائي واليدوي للاختبارات.
#define BOOST_TEST_MODULE MyTest#include <boost/test/included/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(MathTests)
BOOST_AUTO_TEST_CASE(TestSum) { BOOST_CHECK_EQUAL(2 + 2, 4); BOOST_CHECK_CLOSE(3.14159, 3.14, 0.1); // دقة 0.1%}
BOOST_AUTO_TEST_CASE(TestFail) { BOOST_REQUIRE_MESSAGE(false, "This test always fails");}
BOOST_AUTO_TEST_SU