اختبار C++: الوحدة والتكامل والنهاية إلى النهاية من الألف إلى الياء

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

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

ابدأ الدورة

مقدمة: لماذا الاختبار في C++ ليس ترفاً بل ضرورة



في عالم C++، جودة الكود أمر بالغ الأهمية. تمنح اللغة المطور سيطرة هائلة على الذاكرة والأداء، لكن هذه السيطرة تتطلب مسؤولية عالية. خطأ في إدارة الذاكرة، أو استخدام غير صحيح للمؤشرات، أو التعامل غير السليم مع تعدد العمليات (multithreading) يمكن أن يؤدي إلى أخطاء يصعب اكتشافها تظهر فقط في بيئة الإنتاج (production).



الاختبار هو الطريقة الوحيدة الموثوقة لضمان أن كود C++ الخاص بك يعمل كما هو مقصود. في هذه المقالة، سننتقل من اختبارات الوحدة (unit tests) الصغيرة إلى الفحوصات الشاملة من البداية إلى النهاية (E2E). ستتعلم كيفية بناء استراتيجية اختبار صحيحة، وما هي الأدوات التي يجب استخدامها، وكيفية كتابة اختبارات تحقق فائدة حقيقية.



سننظر في ثلاثة مستويات من هرم الاختبار: unit (اختبار الوحدة)، integration (اختبار التكامل)، و e2e (الاختبار الشامل). كل منها يحل مشكلة معينة، وفقط من خلال الجمع بينها نحصل على ثقة كاملة في موثوقية البرنامج.



اختبار الوحدة (Unit Testing): أساس الجودة



اختبارات الوحدة هي المستوى الأدنى والأسرع. إنها تختبر دوالاً أو فئات أو دوال عضوية (methods) فردية بمعزل عن التبعيات الخارجية (قواعد البيانات، نظام الملفات، الشبكة). الهدف الرئيسي هو التأكد من أن كل وحدة منطقية من الكود تعمل بشكل صحيح.



اختيار إطار العمل (Framework) لـ C++



في بيئة C++، هناك العديد من أطر العمل الشائعة لاختبار الوحدة:



  • Google Test (gtest) — إطار العمل الأكثر انتشاراً من Google. يدعم التأكيدات (assertions)، والتركيبات (fixtures)، والاختبارات المعلمية (parameterized tests)، والكائنات الوهمية (mocks) عبر Google Mock.
  • Catch2 — إطار عمل خفيف وحديث مع بناء جملة ممتاز. مناسب بشكل خاص للنمذجة الأولية السريعة للاختبارات.
  • Boost.Test — جزء من مكتبة Boost. قوي ولكنه أثقل وزناً.


كمثال، سنستخدم Google Test لأنه المعيار الفعلي (de facto) في العديد من مشاريع C++.



مثال على اختبار وحدة في C++



لنفترض أن لدينا دالة بسيطة لحساب المضروب (factorial):



// math_utils.h#ifndef MATH_UTILS_H#define MATH_UTILS_H

long long factorial(int n);

#endif

// math_utils.cpp#include "math_utils.h"

long long factorial(int n) { if (n < 0) throw std::invalid_argument("Negative input"); if (n == 0) return 1; long long result = 1; for (int i = 1; i <= n; ++i) { result *= i; } return result;}


الآن سنكتب اختبار وحدة باستخدام Google Test:



// test_math_utils.cpp#include <gtest/gtest.h>#include "math_utils.h"

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

TEST(FactorialTest, HandlesPositiveInput) { EXPECT_EQ(factorial(1), 1); EXPECT_EQ(factorial(5), 120); EXPECT_EQ(factorial(10), 3628800);}

TEST(FactorialTest, ThrowsOnNegative) { EXPECT_THROW(factorial(-1), std::invalid_argument);}


يختبر هذا الاختبار ثلاثة سيناريوهات: الحالة الحدية (0)، العمل الطبيعي، ومعالجة الأخطاء. لاحظ الكليشيهات (macros) EXPECT_EQ و EXPECT_THROW — فهي لا تقطع الاختبار عند حدوث خطأ، بل تسجله فقط.



الكائنات الوهمية (Mock Objects) للعزل



لاختبار الوحدة، من المهم جداً عزل الكود المختبر عن التبعيات الخارجية. لهذا الغرض، تُستخدم الكائنات الوهمية. Google Mock (جزء من Google Test) يسمح بإنشاء النماذج البديلة (stubs) بسهولة:



// database_connector.hclass DatabaseConnector {public:    virtual ~DatabaseConnector() = default;    virtual bool connect(const std::string& url) = 0;    virtual std::vector<int> fetchData() = 0;};

// user_service.cppclass UserService { DatabaseConnector& db;public: UserService(DatabaseConnector& db) : db(db) {} int ge

المدونات

توصيات الكتب