مولد تست یک نرمافزار مخصوص هر ترک است که تستهای یک تمرین عملی را بهطور خودکار تولید میکند. این کار را با تبدیل موارد تست JSON تمرین به تستهایی به زبان همان ترک انجام میدهد.
داشتن یک مولد تست چند مزیت دارد:
بهطور کلی، مولد تست را برای یکی از این دو کار اجرا میکنند:
افزودن یک مولد تست برای یک تمرین جدید به شما امکان میدهد فایل(های) تست آن را تولید کنید. به شرطی که خود مولد تست از قبل پیادهسازی شده باشد، تولید تستهای تمرین جدید (بسیار) کمزحمتتر از نوشتن آنها از صفر است.
وقتی تمرینی مولد تست داشته باشد، میتوانید آن را دوباره اجرا کنید تا تمرین را با آخرین دادهی متعارفش بهروزرسانی یا همگام کنید. توصیه میکنیم این کار را بهصورت دورهای انجام دهید تا ببینید آیا موارد تست مشکلداری هست که باید بهروزرسانی شوند یا تستهای جدیدی هست که ممکن است بخواهید اضافه کنید.
هنگام پیادهسازی مولد تست برای یک تمرین، دو نقطهی شروع ممکن وجود دارد:
اگر تستهای موجودی وجود دارد، مولد تست را طوری پیادهسازی کنید که تستهایی که تولید میکند، راهحلهای موجود را خراب نکند.
بهطور کلی، فایلهای تست به یکی از این دو روش تولید میشوند:
ما دیدهایم که رویکرد مبتنی بر کد به کد مولد تست نسبتاً پیچیدهای منجر میشود، در حالی که رویکرد مبتنی بر قالب سادهتر است.
روشی که توصیه میکنیم این جریان است:
tests.toml تمرین با include = false علامت خوردهاند، کنار بگذاریدمزیت اصلی این ساختار این است که هر تمرین قالب خودش را دارد، که:
هنگام طراحی مولد تست، بکوشید:
مولد تست معمولاً (بیشتر) به زبان همان ترک نوشته میشود.
هرچند آزادید از زبانهای دیگر استفاده کنید، هر زبان اضافهای نگهداری یا مشارکت در ترک را دشوارتر میکند. بنابراین توصیه میکنیم تا جای ممکن از زبان خود ترک استفاده کنید، چون نگهداری و مشارکت را آسانتر میکند.
اگر ترک شما ابزاری برای قالببندی کد دارد، بهتر است آن را بهعنوان مرحلهی پسپردازش، بعد از رندر کردن قالب اجرا کنید.
دادهی اصلیای که مولد تست با آن کار میکند، فایل 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"، چون ممکن است زبان ترک شما از یونیکد پشتیبانی نکند.
فهرست کامل سناریوها را اینجا میبینید.
چند گزینه برای خواندن فایلهای canonical-data.json وجود دارد:
problem-specifications بگیرید (مثلاً https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/leap/canonical-data.json).problem-specifications را بهعنوان سابماژول گیت به مخزن ترک اضافه کنید.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 را به ابزاری عالی برای ترکیب با مولد تست و ساختن جریانهای کاری واقعاً قدرتمند تبدیل میکند.
میخواهید استفاده از مولد تست هم آسان و هم قدرتمند باشد. برای همین، توصیه میکنیم یک یا چند فایل اسکریپت بسازید.
آزادید هر قالب فایل اسکریپتی را که بهترین تناسب را با ترک شما دارد انتخاب کنید. اسکریپتهای شل و اسکریپتهای PowerShell دو گزینهی رایجاند که هر دو خوب کار میکنند.
اینجا نمونهای از یک اسکریپت شل را میبینید که 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 ترک یا پوشهی کد مولد تست بجویید.