مولّد الاختبارات هو أداة برمجية خاصة بمسار معيّن، وظيفتها توليد اختبارات تمرين تطبيقي تلقائيًا. وهي تفعل ذلك بتحويل حالات اختبار التمرين بصيغة JSON إلى اختبارات بلغة المسار.
من مزايا وجود مولّد اختبارات:
عمومًا، يُشغّل المرء مولّد الاختبارات لأحد غرضين:
إضافة مولّد اختبارات لتمرين جديد تتيح لك توليد ملف (أو ملفات) اختباراته. وبافتراض أن مولّد الاختبارات نفسه قد نُفّذ بالفعل، فإن توليد اختبارات التمرين الجديد سيكون عملًا أقل بكثير من كتابتها من الصفر.
بمجرد أن يصبح لتمرين ما مولّد اختبارات، يمكنك تشغيله من جديد لتحديث التمرين ومزامنته مع أحدث بياناته القياسية. نوصي بالقيام بذلك بشكل دوري، للتحقق مما إذا كانت هناك حالات اختبار إشكالية تحتاج إلى تحديث، أو اختبارات جديدة قد ترغب في إضافتها.
هناك نقطتان محتملتان للبداية عند تنفيذ مولّد اختبارات لتمرين:
إذا كانت هناك اختبارات قائمة، فنفّذ مولّد الاختبارات بحيث لا تُعطّل الاختبارات التي يولّدها الحلول الموجودة.
بوجه عام، يعتمد توليد ملفات الاختبار على أحد أمرين:
اتضح لنا أن النهج القائم على الكود يؤدي إلى كود مولّد اختبارات معقّد نسبيًا، في حين أن النهج القائم على القوالب أبسط.
وما نوصي به هو التسلسل التالي:
include = false في ملف tests.toml الخاص بالتمرينالميزة الأساسية لهذا الترتيب هي أن لكل تمرين قالبه الخاص، وهذا:
عند تصميم مولّد الاختبارات، حاول أن:
عادةً ما يُكتب مولّد الاختبارات (في معظمه) بلغة المسار.
يمكنك بحرية استخدام لغات أخرى، لكن كل لغة إضافية ستجعل صيانة المسار أو المساهمة فيه أصعب. لذلك نوصي باستخدام لغة المسار حيث أمكن، لأن ذلك يسهّل الصيانة والمساهمة.
إذا كان لمسارك أدوات لتنسيق الكود، ففكّر في تشغيلها كخطوة معالجة لاحقة بعد تصيير قالبك.
البيانات الأساسية التي يعمل عليها مولّد الاختبارات هي ملف canonical-data.json الخاص بالتمرين.
يُعرَّف هذا الملف في مستودع exercism/problem-specifications، الذي يحدد بيانات وصفية مشتركة لكثير من تمارين Exercism.
ليست كل التمارين لديها ملف canonical-data.json!
وإذا لم يكن لديها، فستحتاج إلى إنشاء الاختبارات يدويًا، إذ لا توجد بيانات ليعمل عليها مولّد الاختبارات.
تُعرَّف البيانات القياسية في كائن JSON.
يحتوي هذا الكائن على حقل "cases" يضم حالات الاختبار.
وهذه الحالات (عادةً) تقابل اختبارات في مسارك واحدًا بواحد.
لكل حالة اختبار عدة خصائص، وأهمها الوصف، والخاصية، وقيم المدخلات، والقيمة المتوقعة. إليك مثالًا (جزئيًا) على ملف canonical-data.json الخاص بتمرين leap:
{
"exercise": "leap",
"cases": [
{
"uuid": "6466b30d-519c-438e-935d-388224ab5223",
"description": "year not divisible by 4 in common year",
"property": "leapYear",
"input": {
"year": 2015
},
"expected": false
},
{
"uuid": "4fe9b84c-8e65-489e-970b-856d60b8b78e",
"description": "year divisible by 4, not divisible by 100 in leap year",
"property": "leapYear",
"input": {
"year": 1996
},
"expected": true
}
]
}
تتمثل المسؤولية الأساسية لمولّد الاختبارات في تحويل بيانات JSON هذه إلى اختبارات خاصة بالمسار. وإليك كيف يمكن ترجمة JSON أعلاه إلى كود اختبار بلغة Nim:
import unittest
import leap
suite "Leap":
test "year not divisible by 4 in common year":
check isLeapYear(2015) == false
test "year divisible by 4, not divisible by 100 in leap year":
check isLeapYear(1996) == true
بنية ملف canonical-data.json موثّقة جيدًا، وله أيضًا تعريف مخطط JSON.
تستخدم بعض التمارين التداخل في بياناتها القياسية.
هذا يعني أن كل عنصر في مصفوفة cases يمكن أن يكون:
يمكنك تمييز نوع العنصر بالتحقق من وجود حقول خاصة بنوع واحد من العناصر.
وأفضل وسيلة لذلك على الأرجح هي استخدام مفتاح "cases"، فهو لا يظهر إلا في مجموعات حالات الاختبار.
إليك مثالًا على حالات اختبار متداخلة:
{
"cases": [
{
"uuid": "e9c93a78-c536-4750-a336-94583d23fafa",
"description": "data is retained",
"property": "data",
"input": {
"treeData": ["4"]
},
"expected": {
"data": "4",
"left": null,
"right": null
}
},
{
"description": "insert data at proper node",
"cases": [
{
"uuid": "7a95c9e8-69f6-476a-b0c4-4170cb3f7c91",
"description": "smaller number at left node",
"property": "data",
"input": {
"treeData": ["4", "2"]
},
"expected": {
"data": "4",
"left": {
"data": "2",
"left": null,
"right": null
},
"right": null
}
}
]
}
]
}
إذا كان مسارك لا يدعم تجميع الاختبارات، فستحتاج إلى:
cases أو تسويته لتحصل في النهاية على حالات الاختبار الأعمق (الأوراق) فقطيختلف محتوى مفتاحي input وexpected في حالة الاختبار اختلافًا واسعًا.
وفي معظم الحالات تكون قيمًا مفردة (مثل الأعداد أو القيم المنطقية أو السلاسل النصية) أو كائنات بسيطة.
لكنك قد تجد أحيانًا قيمًا أكثر تعقيدًا تحتاج على الأرجح إلى بعض المعالجة المسبقة، مثل دوال لامبدا في الكود الزائف، أو قوائم من العمليات التي تُنفَّذ على كود الطالب، وغير ذلك.
لحالات الاختبار حقل اختياري هو scenarios.
ويمكن لمولّد الاختبارات استخدام هذا الحقل لمعالجة حالات اختبار معيّنة معالجة خاصة.
وأكثر استخدام شيوعًا هو تجاهل أنواع معيّنة من الاختبارات، كالاختبارات ذات السيناريو "unicode"، إذ قد لا تدعم لغة مسارك Unicode.
تجد القائمة الكاملة للسيناريوهات هنا.
هناك عدة خيارات لقراءة ملفات canonical-data.json:
problem-specifications (مثل https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/leap/canonical-data.json).problem-specifications كوحدة فرعية Git إلى مستودع المسار.configlet المؤقتة.
ويتوقف الموقع على نظام المستخدم، لكن يمكنك استخدام configlet info -o -v d | head -1 | cut -d " " -f 5 للحصول على الموقع برمجيًا.إذا أراد مسارك إضافة بعض حالات الاختبار الإضافية الخاصة به (غير الموجودة في البيانات القياسية)، فأحد الخيارات هو إنشاء ملف additional-test-cases.json، يمكن لمولّد الاختبارات حينها دمجه مع ملف canonical-data.json قبل تمريره إلى القالب للتصيير.
محرّك القوالب المستخدم سيكون على الأرجح خاصًا بالمسار. ومن المثالي أن تكون قوالبك مباشرة قدر الإمكان، فلا تقلق بشأن تكرار الكود وما شابه.
تحصل القوالب نفسها على بياناتها من مولّد الاختبارات، الذي يمرّ على هذه البيانات لتصيير القوالب.
للمساعدة في إبقاء القوالب بسيطة، قد يكون من المفيد إجراء قليل من المعالجة المسبقة على جانب مولّد الاختبارات، أو تعريف بعض «المرشّحات» أو أي آلية توسعة تسمح بها قوالبك.
configlet هي الأداة الأساسية لصيانة المسار، ويمكن استخدامها من أجل:
bin/configlet create --practice-exercise <slug>
tests.toml لتمرين قائم: شغّل bin/configlet sync --tests --update --exercise <slug>
وهذا يجعل من configlet أداة رائعة للاستخدام مع مولّد الاختبارات في سير عمل قوي حقًا.
ستريد أن يكون استخدام مولّد الاختبارات سهلًا _و_قويًا في آن واحد. ولذلك نوصي بإنشاء ملف سكربت أو أكثر.
لك حرية اختيار صيغة ملف السكربت الأنسب لمسارك. وسكربتات Shell وسكربتات PowerShell خياران شائعان وكلاهما يعمل جيدًا.
إليك مثالًا على سكربت shell يجمع بين configlet ومولّد اختبارات لإنشاء هيكل تمرين جديد بسرعة:
bin/fetch-configlet
bin/configlet create --practice-exercise <slug>
path/to/test-generator <slug>
قبل أن تبدأ في بناء مولّد اختبارات، نقترح أن تطّلع على بضعة مولّدات اختبارات موجودة لتأخذ فكرة عن كيفية تنفيذ المسارات الأخرى لها:
إذا كانت لديك أي أسئلة، فإن المنتدى هو أفضل مكان لطرحها. وقد تفيدك أيضًا نقاشات المنتدى حول مولّد اختبارات Rust ومولّد اختبارات JavaScript.
نوصي ببناء مولّد الاختبارات تدريجيًا، بدءًا من الحد الأدنى من المنتج القابل للاستخدام.
وأبسط نسخة ممكنة ستقرأ ملف canonical-data.json الخاص بالتمرين وتمرّر تلك البيانات إلى القالب فقط.
ابدأ بالتركيز على تمرين واحد، ويُفضّل أن يكون بسيطًا مثل leap.
ولا تبدأ في إضافة المزيد من التمارين تدريجيًا إلا بعد أن يعمل ذلك بنجاح.
وحاول أن تُبقي مولّد الاختبارات بسيطًا قدر الإمكان.
من المثالي أن يتمكن المساهم من لصق قالب موجود أو تعديله دون الحاجة إلى فهم كيفية عمل مولّد الاختبارات من الداخل.
تختلف كيفية استخدام مولّد الاختبارات أو المساهمة فيه من مسار لآخر.
ابحث عن التعليمات في ملف README.md أو CONTRIBUTING.md الخاص بالمسار، أو في مجلد كود مولّد الاختبارات.