پنهان کردن تلههای آزمایشی پنهان در Go: اجتناب از مثبتهای کاذب

کابوس در آزمایش مثبت کاذب خواهد بود. «همه چیز در حال گذر است! شگفت انگیز!» تا اینکه در زمان نامعلومی در آینده همه مین ها با هم منفجر می شوند و تیم شما را به جهنم می برند.
دلایل زیادی وجود دارد که آزمایش ها می توانند بی سر و صدا شکست بخورند.
امروز، من قصد دارم در مورد یک دلیل بسیار اساسی صحبت کنم: نمی دانم تست ها کدامند.
اکثر مردم نیمه راه به پروژه Go ملحق می شوند. بیشتر مردم یک زبان را با استفاده از آن در زندگی واقعی یاد می گیرند.
بنابراین، زمانی که شخصی پروژه را با یک چارچوب آزمایشی مانند testify
، به احتمال زیاد فکر می کنید روش هایی مانند موارد زیر تست هستند.
func (suite *ExampleTestSuite) TestExample() {
suite.Equal(5, suite.VariableThatShouldStartAtFive)
}
سپس روش دیگری مانند آن را اضافه می کنید TestAnotherCase
و پیدا کنید که کار می کند. شما فکر می کنید که کاملاً واضح است که آزمایشات چیست.
آزمایشی که شما در حال صحبت کردن آن هستید ممکن است همان آزمایشی نباشد که بسته Go در حال صحبت کردن است.
از ساخته شده در testing
بسته، تست هر تابعی از فرم است
func TestXxx(*testing.T)
البته از آنجایی که ساخته شده است testing
بسته دارای ویژگی های محدودی است، اکثر پروژه ها از آن استفاده می کنند testify/suite
یا سایر بسته های شخص ثالث مشابه به عنوان چارچوب آزمایشی آنها. تست از testify/suite
نقطه نظر؟
هر روشی که با “تست” شروع می شود را برای افزودن تست ها اضافه کنید
ببینید، ما دو تعریف متفاوت از آزمون داریم.
مشکل هنگام استفاده از ابزار تست شخص ثالث شروع می شود
هنگام استفاده از برخی ابزارها مانند mockery
، مطالب زیر را خواهید خواند
شما نگران فراموش کردن آن نخواهید بود
AssertExpectations
فراخوانی روش دیگر… TheAssertExpectations
روش برای فراخوانی در پایان آزمون ها ثبت شده است
عالیه “بنابراین من فقط نیاز به ایجاد یک ماکت دارم و بسته زمانی که رفتارهای مورد انتظار اتفاق می افتد به من اطلاع می دهد.”
آنجا تله است.
چه زمانی mockery
می گوید at the end of the tests
، در واقع به معنای تعریف از testing
، نه تعریف از testify/suite
.
بنابراین وقتی کد زیر را داشته باشید، هر دو را خواهید دید TestA
و TestB
حتی باید هر دوی آنها از کار بیفتند زیرا راه اندازی ساختگی در آن وجود دارد TestA
استفاده می شود در TestB
.
package mockandsubtest
import (
"fmt"
"testing"
"github.com/stretchr/testify/suite"
)
// Prod code
type ExternalService interface {
Work()
}
type Server struct {
externalService ExternalService
}
func NewServer(externalService ExternalService) *Server {
return &Server{
externalService: externalService,
}
}
// Test code
type ServerSuite struct {
suite.Suite
ExternalService *MockExternalService
Server
}
func TestServerSuite(t *testing.T) {
suite.Run(t, &ServerSuite{})
}
// Run before all test cases
func (s *ServerSuite) SetupSuite() {
s.ExternalService = NewMockExternalService(s.T())
s.Server = Server{externalService: s.ExternalService}
}
// In this test, Work is set up to be called once but not called
func (s *ServerSuite) TestA() {
fmt.Println("TestA is running")
s.ExternalService.EXPECT().Work().Times(1)
}
// In this test, Work is called once unexpectedly
func (s *ServerSuite) TestB() {
fmt.Println("TestB is running")
s.Server.externalService.Work()
}
نتیجه اجرای کد بالا می باشد
TestA is running
TestB is running
PASS
توضیح
فقط معلوم می شود TestServerSuite
به عنوان یک آزمون از testing
و mockery
دیدگاه به همین دلیل است AssertExpectations
در پایان نامیده می شود TestServerSuite
، حتی اگر TestA
و TestB
به صورت داخلی توسط testify/suite
.
از mockery
دیدگاه، s.ExternalService
انتظار می رود که یک بار و در واقع یک بار در چرخه حیات فراخوانی شود TestServerSuite
. بنابراین انتظار برآورده می شود.
چگونه کاهش دهیم؟
دو راه برای پر کردن شکاف بین آنها وجود دارد testify/suite
و testing
.
راه اول ایجاد یک ماک جدید قبل از هر روش تست مانند زیر است.
func (s *ServerSuite) SetupTest() {
s.ExternalService = NewMockExternalService(s.T())
}
گاهی اوقات، به دلایل بسیاری مانند راه اندازی یک نمونه سرور برای هر مورد آزمایشی بسیار گران است، در پروژه شما عملی نیست. سپس می توانید جهت دیگر را امتحان کنید، که پس از هر آزمون به صورت دستی ادعا می شود.
مورد دوم در حال افزودن یک تماس از AssertExpectations
در پایان هر روش تست مثلا زنگ بزن AssertExpectations
در TearDownTest
، که بعد از هر روش تست اجرا می شود.
func (s *ServerSuite) TearDownTest() {
s.ExternalService.AssertExpectations(s.T())
}