سفارشیسازی نحوه اجرای تستها
قبل از اینکه ادامه دهیم و تستهای بیشتری به برنامه خود اضافه کنیم، میخواهم یک استراحت کوتاه بگیرم و درباره چند flag و گزینه مفید که برای سفارشیسازی نحوه اجرای تستهای شما در دسترس است صحبت کنم.
کنترل اینکه کدام تستها اجرا شوند
تا اینجا در این کتاب تستها را در یک package خاص (package cmd/web) به این شکل اجرا کردهایم:
$ go test ./cmd/web
اما همچنین میتوان همه تستهای پروژه فعلی را با استفاده از الگوی wildcard ./... اجرا کرد. در مورد ما، میتوانیم از آن برای اجرای همه تستهای پروژه خود به این شکل استفاده کنیم:
$ go test ./... ok snippetbox.alexedwards.net/cmd/web 0.007s ? snippetbox.alexedwards.net/internal/models [no test files] ? snippetbox.alexedwards.net/internal/validator [no test files] ? snippetbox.alexedwards.net/ui [no test files]
یا در جهت دیگر، میتوان فقط تستهای خاص را با استفاده از flag -run اجرا کرد. این به شما امکان میدهد یک عبارت منظم مشخص کنید — و فقط تستهایی که نام آنها با عبارت منظم مطابقت دارد اجرا میشوند.
برای مثال، میتوانیم فقط تست TestPing را به این شکل اجرا کنیم:
$ go test -v -run="^TestPing$" ./cmd/web/ === RUN TestPing --- PASS: TestPing (0.00s) PASS ok snippetbox.alexedwards.net/cmd/web 0.008s
و حتی میتوانید از flag -run برای محدود کردن تست به برخی sub-testهای خاص با استفاده از فرمت {test regexp}/{sub-test regexp} استفاده کنید. برای مثال برای اجرای sub-test UTC از تست TestHumanDate میتوانیم این کار را انجام دهیم:
$ go test -v -run="^TestHumanDate$/^UTC$" ./cmd/web
=== RUN TestHumanDate
=== RUN TestHumanDate/UTC
--- PASS: TestHumanDate (0.00s)
--- PASS: TestHumanDate/UTC (0.00s)
PASS
ok snippetbox.alexedwards.net/cmd/web 0.003s
در مقابل، میتوانید با استفاده از flag -skip از اجرای تستهای خاص جلوگیری کنید. مانند flag -run که تازه بررسی کردیم، این به شما امکان میدهد یک عبارت منظم مشخص کنید و هر تستی که نام آن با عبارت منظم مطابقت دارد اجرا نخواهد شد. برای مثال، برای رد کردن تست TestHumanDate:
$ go test -v -skip="^TestHumanDate$" ./cmd/web/ === RUN TestPing --- PASS: TestPing (0.00s) === RUN TestCommonHeaders --- PASS: TestCommonHeaders (0.00s) PASS ok snippetbox.alexedwards.net/cmd/web 0.006s
کش تست
احتمالاً تا الان متوجه شدهاید که اگر دقیقاً همان تست را دوبار اجرا کنید — بدون ایجاد هیچ تغییری در package ای که تست میکنید — سپس یک نسخه کش شده از نتیجه تست نمایش داده میشود (که با حاشیهنویسی (cached) در کنار نام package نشان داده میشود).
$ go test ./cmd/web ok snippetbox.alexedwards.net/cmd/web (cached)
در بیشتر موارد، کش کردن نتایج تست واقعاً مفید است (به ویژه برای codebaseهای بزرگ) زیرا به کاهش زمان کل اجرای تست کمک میکند. اما اگر میخواهید تستهای خود را به طور کامل اجرا کنید (و از کش اجتناب کنید) میتوانید از flag -count=1 استفاده کنید:
$ go test -count=1 ./cmd/web
به طور جایگزین، میتوانید نتایج کش شده برای همه تستها را با دستور go clean پاک کنید:
$ go clean -testcache
شکست سریع
همانطور که چند فصل قبل به طور خلاصه ذکر کردم، وقتی از تابع t.Errorf() برای علامتگذاری یک تست به عنوان ناموفق استفاده میکنید، باعث نمیشود که go test فوراً خارج شود. همه تستهای دیگر شما (و sub-testها) پس از یک شکست ادامه خواهند یافت.
اگر ترجیح میدهید تستها را بلافاصله پس از اولین شکست متوقف کنید میتوانید از flag -failfast استفاده کنید:
$ go test -failfast ./cmd/web
مهم است توجه داشته باشید که flag -failfast فقط تستهای در package ای که شکست داشته را متوقف میکند. اگر تستها را در چندین package اجرا میکنید (مثلاً با استفاده از go test ./...)، سپس تستهای packageهای دیگر ادامه خواهند یافت.
تست موازی
به طور پیشفرض، دستور go test همه تستها را به صورت سریالی، یکی پس از دیگری اجرا میکند. وقتی تعداد کمی تست دارید (مثل ما) و زمان اجرا بسیار سریع است، این کاملاً خوب است.
اما اگر صدها یا هزاران تست دارید، زمان کل اجرا میتواند به چیزی معنادارتر تبدیل شود. و در آن سناریو، ممکن است با اجرای تستها به صورت موازی، زمان خود را صرفهجویی کنید.
میتوانید با فراخوانی تابع t.Parallel() در ابتدای تست نشان دهید که اجرای یک تست به صورت همزمان با تستهای دیگر مشکلی ندارد. برای مثال:
func TestPing(t *testing.T) { t.Parallel() ... }
مهم است در اینجا توجه داشته باشید که:
تستهایی که با استفاده از
t.Parallel()علامتگذاری شدهاند به صورت موازی با — و فقط با — تستهای موازی دیگر اجرا میشوند.به طور پیشفرض، حداکثر تعداد تستهایی که به طور همزمان اجرا میشوند، مقدار فعلی GOMAXPROCS است. میتوانید این را با تنظیم یک مقدار خاص از طریق flag
-parallelلغو کنید. برای مثال:$ go test -parallel=4 ./...
همه تستها برای اجرای موازی مناسب نیستند. برای مثال، اگر یک تست یکپارچگی (integration test) دارید که نیاز دارد یک جدول پایگاه داده در یک حالت مشخص شناخته شده باشد، پس نمیخواهید آن را به صورت موازی با تستهای دیگری که همان جدول پایگاه داده را دستکاری میکنند اجرا کنید.
فعالسازی race detector
دستور go test شامل یک flag -race است که race detector گو را هنگام اجرای تستها فعال میکند.
اگر کدی که تست میکنید از همزمانی استفاده میکند، یا تستها را به صورت موازی اجرا میکنید، فعال کردن این میتواند ایده خوبی باشد تا به علامتگذاری race conditionهای موجود در برنامه شما کمک کند. میتوانید آن را به این شکل استفاده کنید:
$ go test -race ./cmd/web/
باید آگاه باشید که race detector در کارایی خود محدود است… این فقط یک ابزار است که data raceها را در صورت شناسایی در زمان اجرا هنگام تست علامتگذاری میکند. این تحلیل استاتیک (static analysis) از codebase شما انجام نمیدهد، و یک اجرای بدون خطا تضمین نمیکند که کد شما عاری از race condition است.
فعال کردن race detector همچنین زمان کل اجرای تستهای شما را افزایش میدهد. بنابراین اگر تستها را به طور مکرر به عنوان بخشی از یک workflow TDD اجرا میکنید، ممکن است ترجیح دهید از flag -race فقط در اجراهای تست pre-commit استفاده کنید.