Let's Go تست › سفارشی‌سازی نحوه اجرای تست‌ها
قبلی · فهرست · بعدی
فصل ۱۳.۴.

سفارشی‌سازی نحوه اجرای تست‌ها

قبل از اینکه ادامه دهیم و تست‌های بیشتری به برنامه خود اضافه کنیم، می‌خواهم یک استراحت کوتاه بگیرم و درباره چند 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()

    ...
}

مهم است در اینجا توجه داشته باشید که:

فعال‌سازی 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 استفاده کنید.