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:

BASH
sudo apt-get install libgtest-dev

ويندوز: استخدم vcpkg أو جمّع من المصدر.


(2) 2.2 اختبارك الأول

مثال: اختبار دالة جمع بـ Google Test (الصعوبة ⭐)

▶ مثال 2: مثال على الكود (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#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();
}

الخرج:

TEXT 📖 للعرض فقط
[==========] Running 2 tests from 1 test suite.
[  PASSED  ] 2 tests.

نتيجة التشغيل:

TEXT 📖 للعرض فقط
[==========] 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 مثال: اختبار طابور بتركيبات (الصعوبة ⭐⭐)

CPP
#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();
}

الخرج:

TEXT 📖 للعرض فقط
(خرج البرنامج)


5. التطوير الموجه بالاختبار (TDD)

(1) 5.1 ما هو TDD؟

TDD (التطوير الموجه بالاختبار) يعني كتابة الاختبارات أولاً، ثم كتابة الكود.

العملية:

  1. اكتب اختبارًا (يفشل)
  2. اكتب الكود (ينجح)
  3. أعد الهيكلة

(2) 5.2 مثال: TDD لمكدس (الصعوبة ⭐⭐⭐)

TEXT 📖 للعرض فقط
// 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; }
};

الخرج:

TEXT 📖 للعرض فقط
(خرج البرنامج)


6. تمرين: اختبار صنف طالب

▶ مثال 1: اختبار كامل (الصعوبة ⭐⭐⭐)

CPP
#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();
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
(خرج البرنامج)

▶ مثال 3: اختبار وحدة بسيط (الصعوبة ⭐)

CPP
#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;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
multiply Test passed
isPositive Test passed
All tests passed!

❓ أسئلة شائعة

س: هل يمكن لاختبار الوحدات اكتشاف جميع الأخطاء؟ ج: لا. اختبارات الوحدات يمكنها فقط اكتشاف أنواع معروفة من الأخطاء. تحتاج أيضًا اختبارات التكامل واختبارات النظام.


س هل يجب شحن كود الاختبار مع كود الإنتاج؟
ج لا. كود الاختبار يُستخدم عادةً فقط أثناء التطوير.

س كيف أختبر الدوال الخاصة؟
ج - الطريقة 1: اختبر الواجهة العامة (اختبار غير مباشر) - الطريقة 2: استخدم FRIEND_TEST لكشف الأعضاء الخاصين - الطريقة 3: أعد الهيكلة باستخراج الدوال الخاصة في صنف منفصل

📖 ملخص

النقطة الرئيسية الملخص
اختبار الوحدات اختبار أصغر الوحدات
Google Test إطار اختبار C++ الأكثر شعبية
وحدات التأكيد EXPECT_* وASSERT_*
تركيبات الاختبار مشاركة كود التهيئة
TDD اكتب الاختبارات أولاً، ثم الكود

📝 تمارين

  1. أساسي (الصعوبة ⭐): اكتب دالة add(a, b) وأنشئ 3 حالات اختبار بـ Google Test: جمع أعداد موجبة، جمع أعداد سالبة، جمع أصفار.

  2. متوسط (الصعوبة ⭐⭐): اكتب دالة isPalindrome تتحقق مما إذا كانت سلسلة متطابقة. اكتب 5 حالات اختبار على الأقل تغطي مدخلات عادية وحدية وخاصة.

  3. متقدم (الصعوبة ⭐⭐⭐): اكتب اختبارات وحدات كاملة لصنف "حساب بنكي" (بطرق إيداع/سحب/الحصول على الرصيد). اختبر الإيداعات/السحوبات العادية، رفض السحب الزائد، وأمان الخيوط (إذا أمكن).



الدرس التالي: تمرين — نظام إدارة جهات الاتصال (#51)

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%