AI 辅助测试:用单元测试门禁兜住 AI 生成代码的底线
Categories:
حل مشكلة AI 辅助测试 ليس هو تغطية الاختبار، بل هو مشكلة ثقتي في الكود المولد بواسطة AI. يمكن للـ AI أن ينتج كوداً طويلاً في ثلاث دقائق، بينما يحتاج الشخص إلى ساعتين لمراجعته بالكامل؛ فالثلاث دقائق إنتاج والساعتين مراجعة أمر ليس مبالغاً فيه. حلّي هو تغيير كائن المراجعة: لا أقرأ الكود سطراً بسطراً، بل أقرأ مستندات حالات الاختبار بدلاً من ذلك، ثم أبني بوابة اختبار وحدة محلية في بيئة التطوير المحلية، فتحافظ حالات الاختبار الفاشلة على مراقبة AI عن كثب وتصحيح الأخطاء.
صغيرة هي معركة AI 辅助测试 الرئيسية
تُصنف الأخطاء إلى نوعين: أخطاء سهلة الحل، وأخطاء صعبة الحل.
الأخطاء الصعبة الحل عادة ما تنشأ عن نقاط ضعف في التصميم العماري أو الحل، وتتطور تدريجياً إلى عبء تاريخي يؤثر في كل جزء؛ وقد تنشأ أيضاً من طرف ثالث مثل مكتبات الطرف الثالث أو أنظمة التشغيل أو المترجم، وتتطلب معرفة متخصصة واستكشافاً عميقاً لحلها. هذا النوع من المشكلات المعقدة خارج نطاق بحث هذا المقال.
يتناول هذا المقال الأخطاء الصغيرة في دورة حياة المنتجات، باستخدام AI لإنشاء حالات اختبار مساعدة، لحل المشكلات التالية:
- أخطاء يمكن اكتشافها بسرعة
- أخطاء منخفضة المستوى يمكن إصلاحها بتعديلات صغيرة
- أخطاء جديدة تم تقديمها بسبب التعديلات
- أخطاء أثناء تعديلات طارئة بسبب الإرهاق
- توازن تكلفة وكفاءة الاختبار الدقيق
معظم الأخطاء هي أخطاء صغيرة، لكن الأخطاء الصغيرة قد تسبب مشاكل خطيرة. حل الأخطاء الصغيرة محلياً يوفر طاقة كبيرة لاحقاً.
تحويل كائن المراجعة من الكود إلى مستندات حالات الاختبار
حاولت فكرة متهورة: منح AI سلطة كاملة لإكمال متطلبات دون مراجعة أي كود مولد. ثم تراجعت. السبب الجذري هو عدم اليقين.
لبرنامج ثنائي ذو منطق حازم ونظيف، يعني عدم اليقين فقدان الثقة في الأجزاء المتداخلة والموجهة للعميل. لذلك أتعلم التفكير في vibe coding: يجب أن تكون لدي يقين نفسي دائماً حول الكود المولد بواسطة AI.
المشكلة في تكلفة المراجعة. قد ينتج AI أو يعدل كوداً كبيراً جداً في ثلاث دقائق، بينما يحتاج الشخص إلى ساعتين لمراجعته بالكامل؛ فالثلاث دقائق إنتاج والساعتين مراجعة أمر ليس مبالغاً فيه. تكون حجم عمل اختبار الكود عادة أكبر من كود العمل، ولا يُحتسب غالباً في تقييم التسليم البحثي، وهذا أحد أسباب عدم رغبة الكثيرين في كتابة اختبارات وحدوية نشطة. لكن AI 助手 أكثر ذكاءً بكثير مني حقيقة، فبدلاً من مقاومته، أفضل تزويده ببوابة.
تحويل كائن المراجعة من الكود إلى مستندات حالات الاختبار هو المفتاح لتجاوز هذه العقدة. مختلفاً عن كود العمل، تصف حالات اختبار الكود المولد بواسطة AI عادة سيناريوهات محددة جداً، يسهل مراجعتها والتحقق منها. كثير من الحالات حتى لا تحتاج قراءتها سطراً بسطراً، بل تنتظر حتى ترتكب خطأ ثم تقرأ بعناية. قول إننا لا نراجع أبداً كود AI مجرد مبالغة، لكن باستخدام مستندات حالات اختبار متكاملة، يمكن إثبات أنه لا يتطلب كل سطر مراجعة بشرية.
---
title: من مراجعة الكود إلى مراجعة المستندات
---
flowchart TB
A[AI ينتج كوداً كبيراً] e1@--> B[مراجعة سطرية مكلفة]
B e2@--> C{ماذا نراجع}
C e3@-->|الكود| D[مراجعة تستغرق ساعتين، بدون يقين]
C e4@-->|مستندات الحالات| E[سهلة المراجعة، التحقق سريع]
E e5@--> F[بوابة اختبار وحدة]
F e6@--> G[الحالات الفاشلة تكشف المشاكل]
classDef risk fill:#FFF3E0,stroke:#E65100,color:#E65100;
classDef ok fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20;
classDef animate stroke:#EF6C00,stroke-width:2px,stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class D risk;
class E,F,G ok;
class e1,e2,e3,e4,e5,e6 animate;أساس المنهجية: Six Sigma و DMAIC
Six Sigma هو منهجية تحسين الجودة المدفوعة بالبيانات، الهدف الأساسي هو تقليل التباين في العمليات، وتحكم معدل العيوب في 3.4 لكل مليون، أي مستوى 6σ1.
يوفر إجراءات DMAIC قابلة للتنفيذ:
- Define: تحليل المتطلبات، تحويلها إلى نماذج بيانات، تصميم هياكل البيانات وبنياتها
- Measure: مراقبة الحالات، التغطية، الأداء، الأمان
- Analyze: تحليل الحالات وكود العمل
- Improve: تحسين الحالات وكود العمل
- Control: بوابة اختبار تلقائية
الأدوات التقنية المرافقة تشمل المُلَّت، والتحقق الثابت، والتحقق الديناميكي وأطر الاختبار. مؤشرات جودة الكود المتاحة كثيرة، مثل مؤشرات التعقيد (التعقيد الدائري، التعقيد الإدراكي، عمق المستويات المتداخلة، ترابط الفئات)، ومؤشرات الجودة (معدل التكرار، طول الدالة، الأرقام السحرية)، ومؤشرات الاختبار (تغطية اختبار الوحدة، نجاح اختبار التكامل، فعالية الحالة، تغطية التعامل مع الاستثناء).
المنهجية والأدوات تساعد في اكتشاف وحل المشاكل، لكن الثمن هو زيادة تكلفة التسليم وتقليل الكفاءة. في الموارد المحدودة، يجب اختيار منهجية تجني فوائد مستدامة قصيرة وطويلة المدى. اخترت اختبار الوحدة كأداة مساعدة في عملية التطوير.
باستخدام AI للتحويل بين اللغات المختلفة للأدوار، يمكن للمطوّرين دمج مزايا TDD، BDD، ATDD، وإنتاج حالات اختبار وجداول حالات بسرعة، مما يحسن جودة التسليم.
اختبار مدفوع: TDD/BDD/ATDD وكود قابل للاختبار
| البعد | TDD | BDD | ATDD |
|---|---|---|---|
| مصدر الدفع | اختبار وحدة | سيناريو سلوك المستخدم | شروط قبول |
| المشاركون | مطور | أعمال+مطور | أعمال+اختبار+مطور |
| مستوى الاختبار | اختبار وحدة | اختبار تكامل/منصة | اختبار قبول |
| أمثلة الأدوات | JUnit, pytest | Cucumber, SpecFlow | FitNesse, Robot Framework |
| الإخراج النموذجي | تقرير تغطية الكود | مستند سيناريو مستخدم قابل للتنفيذ | معدل نجاح اختبار القبول |
| اللغة | assert add(2,3) == 5 | سيناريو باللغة الطبيعية (Given-When-Then) | لغة أعمال (جداول الشروط) |
لهوية اختبار الكود أربعة مبادئ أساسية: المسؤولية الواحدة (كل دالة تفعل شيئاً واحداً)، والإدخال عبر المعاملات (تمرير الاعتماديات عبر المعاملات)، والعزل الواجهة (أنواع المدخلات والمخرجات واضحة)، وقابلية المراقبة للوضع (توفير واجهة استعلام للتحقق من الحالة الداخلية).
في تعليمات AI، أوصي بكتابة قابل للتوسيع، مسؤولية واحدة، إدخال عبر المعاملات مباشرة. حاولت سابقاً التحكم في طول الدالة المولدة وطول الملف، وكان التزامه سيئاً جداً؛ لكن بعد اعتماد المسؤولية الواحدة والإدخال عبر المعاملات، أصبحت النتيجة أكثر توافقاً مع المتوقع. التحسين المبكر هو مصدر كل الشرور، عند إنشاء هياكل البيانات الأساسية وبنياتها، اطلب من AI أولوية القابلية للتوسيع.
مثال كود يسهل اختباره:
// user.h
typedef struct {
int id;
char name[50];
int age;
} User;
// 内存存储接口抽象
typedef struct {
User* (*get_user)(int id);
int (*save_user)(const User* user);
} UserStorage;
// 创建用户 (依赖注入存储接口)
User* create_user(const char* name, int age, UserStorage* storage) {
if(strlen(name) >= 50 || age <= 0) return NULL;
User* new_user = malloc(sizeof(User));
new_user->id = generate_user_id();
strncpy(new_user->name, name, 49);
new_user->name[49] = '\0';
new_user->age = age;
if(storage->save_user(new_user) != 0) {
free(new_user);
return NULL;
}
return new_user;
}
مثال كود صعب الاختبار:
// 紧耦合数据库操作
void save_user_to_db(User* user) {
MYSQL conn;
mysql_init(&conn);
if(!mysql_real_connect(&conn, "localhost", "root", "pass", "test", 0, NULL, 0)) {
log_error("DB connection failed"); // 难以测试的日志依赖
exit(1); // 直接退出影响测试执行
}
// 拼接 SQL 并写入数据库
}
الكود الجيد يخترق بأسلوب هادئ، والكود السيء يبرز بوضوح. الدالة سهلة الاختبار لها قيم return واضحة، ومعالجة أخطاء موحدة، ومعاملات مدخلات واضحة، وسلامة التأثيرات الجانبية منفصلة، وتدعم الإدخال عبر المعاملات. قد لا يكون الكود المقدم من AI أفضل الممارسات، وقد تكون له مشاكل في الأسلوب. رغم أنه غالباً ما يحل المشاكل، إلا أن المشروع الحقيقي ليس صفقة واحدة، ويجب مراعاة الصيانة اللاحقة. عند توجيه AI لإنتاج الكود، يجب التشديد أيضاً على القابلية للقراءة أولاً، قابل للتوسيع، مسؤولية واحدة، إدخال عبر المعاملات. لا نناقش مشاكل الأسلوب أكثر، يمكن الرجوع إلى دليل أسلوب الكود لـ Google2.
حفر وعقبات في التطوير الفعلي
تتعرض المتطلبات لنقل بين الأدوار عبر تحويلات لغوية متعددة، وكل دورة قد تولد انحرافاً في التعبير. مثلاً، يُفهم من قول العميل “مشاهدة مركزية” أن الخادم يعيد “إرجاع عبر واجهة وحدة واحدة” دون تقسيم التصفية. باستخدام AI لإنتاج قوالب لغات الأدوار، يمكن تقليل فقدان المعلومات أثناء التحويل اليدوي.
التفكير غير الكافي هو مصدر العديد من الأخطاء. باستخدام AI لتصميم سيناريوهات الحالات، يمكنه إنجاز المهام المتوقعة، وتقديم إلهام يتجاوز التوقعات، وتزويد التفاصيل التي لم يفكر فيها منظم الإطلاق. في كل دورة نقل لغوي للأدوار، يمكن استخدام المحادثة مع AI لتحسين الثغرات. Markdown هو اللغة الأسهل فهماً للـ AI، ويجب استخدام التنسيق Markdown قدر الإمكان في المدخلات والمخرجات.
لكود الحالات عدة نقاط يجب الانتباه إليها: عزل الحالات، دقة محاكاة الاعتماديات، صلاحية العبارات، القيم الحدية، معالجة الاستثناء، الأداء، التزامن. يلتزم AI بالكسل، ولا ينشئ حالات تغطي كل السيناريوهات دفعة واحدة. إنتاج كل كود اختبار دفعة واحدة يضع متطلبات عالية على النموذج، ويمكن إنشاؤه على عدة جولات. مثلاً، نموذج Claude في Cursor، أنهى بعض النقاط المتعلقة بكود الحالات بشكل جيد، لكنه كثيراً ما ينسى عزل الحالات ومشاهد الاستثناء. إضافة قاعدة مستخدم يمكن أن تقلل قليلاً من الكسل: عند إنشاء حالات اختبار، راجع مستندات الحالات، عزل الحالات. مع ذلك، لا ينشئ جميع السيناريوهات دفعة واحدة، وتتطمس مسارات الطبيعة، ومسارات الاستثناء، والقيم الحدية، والأداء، والتزامن جولات متعددة من المحادثة لإكمالها.
تدفق اختبار مساعد AI قابل للتنفيذ
---
title: تدفق اختبار مساعد AI
---
flowchart TB
A[مستند التصميم] e1@--> B[إنشاء سيناريو اختبار قبول]
B e2@--> C[إنشاء هياكل البيانات وبنياتها]
C e3@--> D[إنشاء جدول حالات الجانب الطرفي]
D e4@--> E[إنشاء كود الحالات]
D e5@--> F[إنشاء كود العمل]
E e6@--> G[بوابة محلية: حالات تمر]
F e7@--> G
G e8@--> H{وجود حالات فاشلة}
H e9@-->|نعم| E
H e10@-->|لا| I[التسليم]
classDef input fill:#E3F2FD,stroke:#1565C0,color:#0D47A1;
classDef gen fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20;
classDef gate fill:#FFF3E0,stroke:#E65100,color:#E65100;
classDef animate stroke:#EF6C00,stroke-width:2px,stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class A input;
class B,C,D,E,F gen;
class G,H,I gate;
class e1,e2,e3,e4,e5,e6,e7,e8,e9,e10 animate;يمكن تنظيم تعليمات كل خطوة كما يلي: بناءً على مستند تصميم المنتج، أنشئ سيناريوهات اختبار قبول؛ بناءً على السيناريوهات، أنشئ هياكل البيانات وبنياتها؛ بناءً على السيناريوهات وبنياتها، أنشئ جدول حالات الجانب الطرفي؛ بناءً على البنيات وجدول الحالات، أنشئ كود الحالات؛ بناءً على السيناريوهات، أنشئ كود العمل مع ضمان مرور الحالات.
هناك مشكلة خفية: قد يعدل AI كود العمل بناءً على كود الاختبار، أو العكس. إذا كانت هناك أخطاء في الجانبين، قد يربك AI، مع زيادة عدد المحادثات، سيصبح كود الاختبار وكود العمل أكثر فوضى، وفي هذه الحالة يجب التدخل يدوياً.
طلب كبير الحجم هو مثال معارض: طلب AI تغطية كل السيناريوهات الطبيعية، والاستثناء، والقيم الحدية، والتزامن وإنتاج الكود دفعة واحدة. مثل هذه الطلبات عرضة للانقطاع. الطريقة الصحيحة هي تقسيم السيناريوهات في Cursor، وإنتاجها على عدة طلبات متعددة. تحتاج حالات اختبار الأداء إلى بيانات كبيرة، يجب وضعها في مرحلة اختبار النظام، ابدأ بمنح AI مولد بيانات عشوائي، ثم استخدم البيانات المولدة لبناء الحالات.
يحتاج اختبار التكامل إلى بناء بيئة اختبار. بعض الحالات تعتمد على أدوات خارجية، مثل عزل micro الذي يعتمد على iptables وipset، يمكن استخدام Docker لبناء حاويات اختبار. القاعدة هي: كيف يختبر الإنسان، يجب أن يوجه AI لتنفيذ نفس الخطوات التلقائية. بالنسبة لاختبار الأمان، يعتمد حالياً على الخبرة والتحليل، ولا توجد خبرة AI مساعدة ناضجة بعد.
الخلاصة
هذا التدفق ينتج المزيد من المستندات وكود اختبار مقارنة بعدم استخدام AI، من الصعب مراجعة كل حرف. لكن المستندات أسهل في المراجعة من الكود، خاصة جدول الحالات، الذي يوجه إنشاء الحالات بدقة. في التطوير الفعلي، أحياناً أراجع فقط حالات الاختبار الفاشلة، للتأكد من المشكلة عبر تصحيحها.
الاتجاهات للتحسين ثلاثة: هناك تداخل في عمليات التصميم الفرعية، قد تعتمد سيناريوهات اختبار القبول على هياكل البيانات وبنياتها؛ جدول حالات الجانب الطرفي يراقبه المطوّرون أنفسهم؛ قبل التسليم، يجب إعادة إنشاء سيناريوهات اختبار مفصلة تشمل تفاصيل كود العمل.
الفائدة النهائية ليست في أرقام تغطية الاختبار، بل في الثقة: باستخدام بوابة اختبار الوحدة هذه المحلية، أصبحت الآن لدي يقين نفسي حول الكود المولد بواسطة AI، بدلاً من إرسال الكود المستخلص vibing بلا تفكير إلى الإنتاج.