ホームページ >バックエンド開発 >Golang >Go に隠されたテストの落とし穴を明らかにする: 誤検知を回避する

Go に隠されたテストの落とし穴を明らかにする: 誤検知を回避する

Susan Sarandon
Susan Sarandonオリジナル
2024-12-26 11:08:14377ブラウズ

Unmasking Hidden Test Pitfalls in Go: Avoiding False Positives

検査における悪夢は偽陽性です。 「すべてのものは過ぎ去っていきます!すばらしい!"将来いつになるか分からないが、すべての地雷が一斉に爆発し、チームを地獄に吹き飛ばすまでは。

テストがサイレントに失敗する理由は数多くあります。

今日は、非常に基本的な理由の 1 つについてお話します。それは、どれがテストなのかわからないということです。

なぜどれがテストなのかわからないのですか?

ほとんどの人は Go プロジェクトに途中から参加します。ほとんどの人は、実生活で言語を使用することで言語を学びます。

したがって、誰かが testify のようなテスト フレームワークを使用してプロジェクトをセットアップした場合、おそらく次のようなメソッドがテストであると考えるでしょう。

func (suite *ExampleTestSuite) TestExample() {
    suite.Equal(5, suite.VariableThatShouldStartAtFive)
}

次に、TestAnotherCase のような別のメソッドを追加すると、それが機能することがわかります。あなたはテストとは何かについては十分に理解していると思います。

テストはフレームワークごとに異なる意味を持ちます

あなたが話している「テスト」は、Go パッケージが話しているテストと同じではない可能性があります。

組み込みのテスト パッケージから、テストは次の形式の関数です

func TestXxx(*testing.T)

もちろん、組み込みのテスト パッケージには機能が制限されているため、ほとんどのプロジェクトは testify/suite またはその他の同様のサードパーティ パッケージをテスト フレームワークとして使用しています。 testify/スイートの観点から見たテストとは何ですか?

「Test」で始まるメソッドを追加してテストを追加します

ほら、テストには 2 つの異なる定義があります。

サードパーティのテストツールを使用すると問題が始まります

嘲笑などのツールを使用する場合は、次の内容をお読みください

もう AssertExpectations メソッドの呼び出しを忘れることを心配する必要はありません…AssertExpectations メソッドはテストの最後に呼び出されるように登録されています

すごいですね! 「つまり、モックを作成するだけで済み、予期した動作が発生したときにパッケージが通知してくれるのです。

そこに罠があります。

テストの最後に嘲笑が言うとき、それは実際には testify/suite からの定義ではなく testing からの定義を意味します。

次のコードがある場合、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 のみがテストとみなされていることがわかりました。 TestA と TestB が testify/suite によって内部的に実行されていても、 AssertExpectations が TestServerSuite の最後に呼び出されるのはこのためです。

モッカリーの観点からは、s.ExternalService は TestServerSuite のライフサイクルで 1 回呼び出されることになっており、実際に 1 回呼び出されます。したがって、期待は満たされます。

軽減するにはどうすればよいですか?

testify/スイートとテストの間のギャップを埋めるには 2 つの方法があります。

最初の方法は、次のように各テスト メソッドの前に新しいモックを作成することです。

func (suite *ExampleTestSuite) TestExample() {
    suite.Equal(5, suite.VariableThatShouldStartAtFive)
}

テスト ケースごとにサーバー インスタンスをセットアップするのにコストがかかりすぎるなど、さまざまな理由により、プロジェクトでは現実的ではない場合があります。その後、各テストの後に手動でアサートする他の方向を試すことができます。

2 つ目は、各テスト メソッドの最後に AssertExpectations の呼び出しを追加することです。たとえば、各テスト メソッドの後に実行される TearDownTest で AssertExpectations を呼び出します。

func TestXxx(*testing.T)

以上がGo に隠されたテストの落とし穴を明らかにする: 誤検知を回避するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。