首頁 > 後端開發 > Golang > 揭露 Go 中隱藏的測試陷阱:避免誤報

揭露 Go 中隱藏的測試陷阱:避免誤報

Susan Sarandon
發布: 2024-12-26 11:08:14
原創
319 人瀏覽過

Unmasking Hidden Test Pitfalls in Go: Avoiding False Positives

測驗中的惡夢將是誤報。 「一切都會過去!驚人的!」直到未來的某個未知時間,所有地雷一起爆炸,將你的團隊炸入地獄。

測試可能默默失敗的原因有很多。

今天我要講一個很基本的原因:不知道哪些是測驗。

為什麼你不知道哪些是測驗?

大多數人都是半途而廢地加入 Go 專案的。大多數人透過在現實生活中使用語言來學習語言。

因此,當有人用testify這樣的測試框架來建立專案時,你很可能會認為下面這樣的方法就是測試。

func (suite *ExampleTestSuite) TestExample() {
    suite.Equal(5, suite.VariableThatShouldStartAtFive)
}
登入後複製
登入後複製

然後您加入另一種方法,例如 TestAnotherCase 並發現它有效。您認為您非常清楚什麼是測試。

測試在不同的框架中有不同的意義

您所說的「測試」可能與 Go 套件所說的測試不同。

從內建的測試包中,測試可以是以下形式的任何函數

func TestXxx(*testing.T)
登入後複製
登入後複製

當然,由於內建的測試包功能有限,大多數專案都使用 testify/suite 或其他類似的第三方套件作為測試框架。從 testify/suite 的角度來看,什麼是測試?

加入任何以「Test」開頭的方法來新增測試

看,我們對測驗有兩種不同的定義。

使用第三方測試工具時就會出現問題

使用mockery等工具時,你會閱讀以下內容

您不必再擔心忘記 AssertExpectations 方法呼叫...AssertExpectations 方法已註冊為在測試結束時呼叫

太棒了! 「所以我只需要創建一個模擬,當預期的行為發生時,包就會通知我」。

那就是陷阱。

當mockery在測試結束時說時,它實際上意味著來自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 才被視為測驗。這就是為什麼在 TestServerSuite 末尾呼叫 AssertExpectations 的原因,即使 TestA 和 TestB 是由 testify/suite 內部執行的。

從mockery的角度來看,s.ExternalService預計會被呼叫一次,並且在TestServerSuite的生命週期中實際上會被呼叫一次。所以期望達到了。

如何緩解?

有兩種方法可以彌補 testify/suite 和測試之間的差距。

第一種方法是在每個測試方法之前建立一個新的模擬,如下所示。

func (suite *ExampleTestSuite) TestExample() {
    suite.Equal(5, suite.VariableThatShouldStartAtFive)
}
登入後複製
登入後複製

有時,由於多種原因,它在您的專案中並不實用,例如為每個測試案例設定一個伺服器實例太昂貴。然後你可以嘗試另一個方向,即每次測試後手動斷言。

第二個是在每個測試方法的最後加上對 AssertExpectations 的呼叫。例如,在 TearDownTest 中呼叫 AssertExpectations,它在每個測試方法之後執行。

func TestXxx(*testing.T)
登入後複製
登入後複製

以上是揭露 Go 中隱藏的測試陷阱:避免誤報的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板