اجرای موازی تستهای PHP روی GitHub Actions
خلاصهٔ کاملتر
اگه یه تستسوییت بزرگ داری و هر بار push کردن یعنی ۱۵-۲۰ دقیقه صبر کردن برای نتیجه تستها، این مطلب دقیقاً برای توئه. Freek Van der Herten از تیم Oh Dear یه تکنیک جالب معرفی کرده که باهاش تونستن زمان اجرای تستها رو از ۱۶ دقیقه به ۴ دقیقه برسونن.
ماجرا از اینجا شروع میشه که Laravel یه قابلیت داره برای اجرای موازی تستها بر اساس تعداد CPU core. مثلاً روی یه مک ۱۰ هستهای، ۱۰ تست همزمان اجرا میشن. ولی مشکل اینه که runner پیشفرض GitHub Actions معمولاً core زیادی نداره، پس این روش اونجا خیلی کارساز نیست.
راهحل اینه که به جای تکیه روی CPU core، از قابلیت matrix strategy خود GitHub Actions استفاده کنیم. یعنی تستسوییت رو به چند بخش مساوی تقسیم میکنیم (مثلاً ۱۲ بخش) و هر بخش رو روی یه job مجزا و به صورت کاملاً موازی اجرا میکنیم.
اول باید لیست کامل تستها رو بگیریم. Pest با آپشن --list-tests این کار رو میکنه. بعد میشه با متد split روی collection اونا رو به بخشهای مساوی تقسیم کرد:
$allTestNames = Str::of($process->getOutput())
->explode("\n")
->filter(fn(string $line) => str_contains($line, ' - '))
->map(function (string $fullTestName) {
return Str::of($fullTestName)
->replace('- ', '')
->trim()
->between('\\\\', '::')
->afterLast('\\')
->toString();
})
->filter()
->unique();بعد از اینکه نام تستها رو داریم، با آپشن --filter میشه فقط تستهای یه بخش مشخص رو اجرا کرد. چند تا pattern رو هم میشه با | از هم جدا کرد:
$process = new Process(
command: ['./vendor/bin/pest', '--filter', $testNames->join('|')],
timeout: null
);در فایل GitHub Actions هم با تعریف matrix، به ازای هر shard_number یه job مجزا ساخته میشه:
strategy:
matrix:
total_shard_count: [12]
shard_number: ['01','02','03','04','05','06','07','08','09','10','11','12']هر job به جای اجرای مستقیم pest، یه اسکریپت PHP سفارشی به نام github_parallel_test_runner رو صدا میزنه. این اسکریپت متغیرهای محیطی TOTAL_SHARD_COUNT و SHARD_NUMBER رو میخونه، لیست تستها رو تهیه میکنه، سهم خودش رو پیدا میکنه و فقط همون تستها رو اجرا میکنه.
یه نکته مهم اینه که Composer و NPM فقط یک بار اجرا میشن و خروجیشون کش میشه تا همه ۱۲ job ازش استفاده کنن. این باعث میشه سربار اجرای موازی کم بشه.
نتیجه نهایی اینه که در داشبورد GitHub میتونی ببینی همه ۱۲ بخش به صورت همزمان در حال اجرا هستن و کل تستسوییت در کسری از زمان قبلی تموم میشه.
نکات کلیدی:
- تیم Oh Dear با این روش زمان تست رو از ۱۶ دقیقه به ۴ دقیقه کاهش دادن
- از matrix strategy در GitHub Actions برای اجرای موازی jobها استفاده میشه
- Pest با آپشن --list-tests لیست کامل تستها رو برمیگردونه
- آپشن --filter به همراه | امکان اجرای چند کلاس تست رو به طور همزمان میده
- یه اسکریپت PHP سفارشی وظیفه تقسیم تستها و اجرای هر shard رو داره
- Composer و NPM فقط یک بار اجرا میشن تا سربار اضافه نداشته باشیم




