C++: اختبار الوحدات
آخر تحديث: 2026-08-26
في الدرس 49، تعلمنا عن تحسين الأداء.
الآن، سنتعلم عن اختبار الوحدات — المهارة الأساسية لضمان جودة الكود.
كتابة الكود بدون اختبار مثل الطبخ بدون تذوق.
1. نظرة عامة على اختبار الوحدات
(1) 1.1 ما هو اختبار الوحدات؟
اختبار الوحدات يتضمن اختبار أصغر الوحدات القابلة للاختبار (الدوال، الأصناف).
المزايا:
- اكتشاف الأخطاء مبكرًا
- شبكة أمان لإعادة الهيكلة
- توثيق سلوك الكود
(2) 1.2 اختيار إطار اختبار
| الإطار | الوصف |
|---|---|
| Google Test | من جوجل، الأكثر شعبية |
| Catch2 | ملف رأس واحد، سهل الاستخدام |
| Boost.Test | جزء من مكتبات Boost |
| Doctest | مشابه لـ Catch2، أسرع |
التوصية: Google Test (غني الميزات) أو Catch2 (بسيط)
2. البدء مع Google Test
(1) 2.1 تثبيت Google Test
Linux:
sudo apt-get install libgtest-dev
ويندوز: استخدم vcpkg أو جمّع من المصدر.
(2) 2.2 اختبارك الأول
مثال: اختبار دالة جمع بـ Google Test (الصعوبة ⭐)
▶ مثال 2: مثال على الكود (الصعوبة ⭐)
#include <gtest/gtest.h>
int add(int a, int b) {
return a + b;
}
TEST(AddTest, Positive) {
EXPECT_EQ(add(2, 3), 5);
}
TEST(AddTest, Negative) {
EXPECT_EQ(add(-2, -3), -5);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
الخرج:
[==========] Running 2 tests from 1 test suite.
[ PASSED ] 2 tests.
نتيجة التشغيل:
[==========] Running 2 tests from 1 test suite.
[ PASSED ] 2 tests.
3. وحدات التأكيد
(1) 3.1 ASSERT مقابل EXPECT
| الماكرو | السلوك عند الفشل |
|---|---|
ASSERT_* |
يُوقف الاختبار الحالي |
EXPECT_* |
يستمر في التشغيل |
التوصية: استخدم EXPECT_* (يمكنك رؤية المزيد من حالات الفشل)
(2) 3.2 التأكيدات الشائعة
| التأكيد | الوصف |
|---|---|
EXPECT_EQ(a, b) |
a == b |
EXPECT_NE(a, b) |
a != b |
EXPECT_LT(a, b) |
a < b |
EXPECT_GT(a, b) |
a > b |
EXPECT_TRUE(cond) |
cond صحيح |
EXPECT_FALSE(cond) |
cond خاطئ |
EXPECT_THROW(statement, exception_type) |
يتوقع رمي استثناء |
4. تركيبات الاختبار
(1) 4.1 لماذا نحتاج التركيبات؟
المشكلة: عدة اختبارات تحتاج نفس كود التهيئة.
الحل: تركيبات الاختبار (ترث من testing::Test)
(2) 4.2 مثال: اختبار طابور بتركيبات (الصعوبة ⭐⭐)
#include <gtest/gtest.h>
#include <queue>
class QueueTest : public testing::Test {
protected:
void SetUp() override {
q.push(1);
q.push(2);
q.push(3);
}
void TearDown() override {
// التنظيف (إذا لزم الأمر)
}
std::queue<int> q;
};
TEST_F(QueueTest, Size) {
EXPECT_EQ(q.size(), 3);
}
TEST_F(QueueTest, Front) {
EXPECT_EQ(q.front(), 1);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
الخرج:
(خرج البرنامج)
5. التطوير الموجه بالاختبار (TDD)
(1) 5.1 ما هو TDD؟
TDD (التطوير الموجه بالاختبار) يعني كتابة الاختبارات أولاً، ثم كتابة الكود.
العملية:
- اكتب اختبارًا (يفشل)
- اكتب الكود (ينجح)
- أعد الهيكلة
(2) 5.2 مثال: TDD لمكدس (الصعوبة ⭐⭐⭐)
// 1. اكتب الاختبار أولاً
TEST(StackTest, PushAndPop) {
Stackint s;
s.push(42);
EXPECT_EQ(s.pop(), 42);
}
// 2. ثم اكتب الكود
template<typename T>
class Stack {
std::vectorT data;
public:
void push(const T& x) { data.push_back(x); }
T pop() { T x = data.back(); data.pop_back(); return x; }
};
الخرج:
(خرج البرنامج)
6. تمرين: اختبار صنف طالب
▶ مثال 1: اختبار كامل (الصعوبة ⭐⭐⭐)
#include <gtest/gtest.h>
#include <string>
class Student {
std::string name;
int age;
public:
Student(const std::string& name, int age) : name(name), age(age) {}
std::string getName() const { return name; }
int getAge() const { return age; }
};
TEST(StudentTest, Constructor) {
Student s("Zhang San", 20);
EXPECT_EQ(s.getName(), "Zhang San");
EXPECT_EQ(s.getAge(), 20);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
الخرج:
(خرج البرنامج)
▶ مثال 3: اختبار وحدة بسيط (الصعوبة ⭐)
#include <iostream>
#include <cassert>
int multiply(int a, int b) {
return a * b;
}
bool isPositive(int n) {
return n > 0;
}
int main() {
assert(multiply(2, 3) == 6);
assert(multiply(-2, 3) == -6);
assert(multiply(0, 5) == 0);
std::cout << "multiply Test passed" << std::endl;
assert(isPositive(5) == true);
assert(isPositive(-1) == false);
assert(isPositive(0) == false);
std::cout << "isPositive Test passed" << std::endl;
std::cout << "All tests passed!" << std::endl;
return 0;
}
الخرج:
multiply Test passed
isPositive Test passed
All tests passed!
❓ أسئلة شائعة
س: هل يمكن لاختبار الوحدات اكتشاف جميع الأخطاء؟ ج: لا. اختبارات الوحدات يمكنها فقط اكتشاف أنواع معروفة من الأخطاء. تحتاج أيضًا اختبارات التكامل واختبارات النظام.
📖 ملخص
| النقطة الرئيسية | الملخص |
|---|---|
| اختبار الوحدات | اختبار أصغر الوحدات |
| Google Test | إطار اختبار C++ الأكثر شعبية |
| وحدات التأكيد | EXPECT_* وASSERT_* |
| تركيبات الاختبار | مشاركة كود التهيئة |
| TDD | اكتب الاختبارات أولاً، ثم الكود |
📝 تمارين
-
أساسي (الصعوبة ⭐): اكتب دالة
add(a, b)وأنشئ 3 حالات اختبار بـ Google Test: جمع أعداد موجبة، جمع أعداد سالبة، جمع أصفار. -
متوسط (الصعوبة ⭐⭐): اكتب دالة
isPalindromeتتحقق مما إذا كانت سلسلة متطابقة. اكتب 5 حالات اختبار على الأقل تغطي مدخلات عادية وحدية وخاصة. -
متقدم (الصعوبة ⭐⭐⭐): اكتب اختبارات وحدات كاملة لصنف "حساب بنكي" (بطرق إيداع/سحب/الحصول على الرصيد). اختبر الإيداعات/السحوبات العادية، رفض السحب الزائد، وأمان الخيوط (إذا أمكن).
- اختبار الوحدات يتحقق من صحة الدوال أو الوحدات
- أطر الاختبار: Google Test / Catch2 / doctest
- بنية حالة الاختبار: ترتيب → تنفيذ → تأكيد
- التطوير الموجه بالاختبار (TDD): اكتب الاختبارات أولاً، ثم الكود
- تغطية الكود تقيس مدى شمول اختباراتك
الدرس التالي: تمرين — نظام إدارة جهات الاتصال (#51)