web-dev-qa-db-ja.com

Goでの単体テストと統合テストの分離

GoLangで単体テストと統合テストを分離するための確立されたベストプラクティスはありますか(testify)?ユニットテスト(外部リソースに依存しないため非常に高速に実行される)と統合テスト(外部リソースに依存するため実行に時間がかかる)が混在しています。したがって、go testと言ったときに統合テストを含めるかどうかを制御できるようにしたいと思います。

最も簡単な手法は、mainで-integrateフラグを定義することです。

var runIntegrationTests = flag.Bool("integration", false
    , "Run the integration tests (in addition to the unit tests)")

そして、すべての統合テストの先頭にifステートメントを追加するには:

if !*runIntegrationTests {
    this.T().Skip("To run this test, use: go test -integration")
}

これは私ができる最善ですか?私はtestifyのドキュメントを検索して、おそらく命名規則またはこれを実現する何かがあるかどうかを確認しましたが、何も見つかりませんでした。何か不足していますか?

73
Craig Jones

@ Ainar-Gは、テストを分離するための優れたパターンをいくつか提案しています。

このSoundCloudのGoプラクティスのセット は、ビルドタグを使用することを推奨します( ビルドパッケージの「ビルドの制約」セクションで説明 )、実行するテストを選択します。

Integration_test.goを作成し、統合のビルドタグを付けます。サービスアドレスや接続文字列などの(グローバル)フラグを定義し、テストで使用します。

// +build integration

var fooAddr = flag.String(...)

func TestToo(t *testing.T) {
    f, err := foo.Connect(*fooAddr)
    // ...
}

go testは、go buildと同様にビルドタグを取得するため、go test -tags=integrationを呼び出すことができます。また、flag.Parseを呼び出すパッケージmainを合成するため、宣言されて表示されているフラグはすべて処理され、テストで使用できます。

同様のオプションとして、ビルド条件// +build !unitを使用してデフォルトで統合テストを実行し、go test -tags=unitを実行してオンデマンドで統合テストを無効にすることもできます。

@adamcコメント:

ビルドタグを使用しようとする他のユーザーにとって、// +build testコメントがファイルの最初の行であり、コメントの後に空白行を含めることが重要です。そうでない場合、-tagsコマンドはディレクティブを無視します。

また、ビルドコメントで使用されるタグにはダッシュを使用できませんが、アンダースコアは使用できます。たとえば、// +build unit-testsは機能しませんが、// +build unit_testsは機能します。

119
Alex

考えられる解決策は3つあります。 1つは、単体テストに shortモード を使用することです。したがって、統合テストを実行するには、go test -shortを単体テストで使用し、-shortフラグを使用せずに使用します。標準ライブラリは、短いモードを使用して、長時間実行されるテストをスキップするか、より単純なデータを提供することでテストをより速く実行します。

2番目は、規則を使用して、テストをTestUnitFooまたはTestIntegrationFooのいずれかで呼び出し、 -runテストフラグ を使用して、実行するテストを示します。したがって、単体テストにはgo test -run 'Unit'を使用し、統合テストにはgo test -run 'Integration'を使用します。

3番目のオプションは、環境変数を使用し、テストセットアップで os.Getenv を使用して取得することです。次に、単純なgo testを単体テストに、FOO_TEST_INTEGRATION=true go testを統合テストに使用します。

私は個人的には-shortソリューションを好みます。それはよりシンプルで標準ライブラリで使用されているため、長期実行テストを事実上分離/簡素化する方法のようです。ただし、-runおよびos.Getenvソリューションは柔軟性が向上します(正規表現が-runに関係するため、さらに注意が必要です)。

43
Ainar-G

@ Ainar-Gの優れた答えに対する私のコメントを詳しく説明するために、過去1年間、-shortIntegration命名規則の組み合わせを使用して、両方の世界のベストを達成してきました。

同じファイル内の単体テストと統合テストの調和

以前、ビルドフラグにより​​、複数のファイル(services_test.goservices_integration_test.goなど)が必要になりました。

代わりに、最初の2つが単体テストであり、最後に統合テストがある次の例をご覧ください。

package services

import "testing"

func TestServiceFunc(t *testing.T) {
    t.Parallel()
    ...
}

func TestInvalidServiceFunc3(t *testing.T) {
    t.Parallel()
    ...
}

func TestPostgresVersionIntegration(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping integration test")
    }
    ...
}

最後のテストには次の規則があることに注意してください。

  1. テスト名にIntegrationを使用します。
  2. -shortフラグディレクティブの下で実行されているかどうかを確認します。

基本的に、仕様は次のようになります。「すべてのテストを通常どおりに記述します。長時間実行されるテストまたは統合テストの場合、この命名規則に従い、-shortがピアにとって素晴らしいことを確認します。」

単体テストのみを実行します。

go test -v -short

これにより、次のような素敵なメッセージセットが提供されます。

=== RUN   TestPostgresVersionIntegration
--- SKIP: TestPostgresVersionIntegration (0.00s)
        service_test.go:138: skipping integration test

統合テストのみを実行します。

go test -run Integration

これは統合テストのみを実行します。本番環境のカナリア煙テストに役立ちます。

明らかに、このアプローチのマイナス面は、誰かがgo testフラグなしで-shortを実行すると、デフォルトですべてのテスト(ユニットテストと統合テスト)が実行されることです。

実際には、プロジェクトがユニットテストと統合テストを行うのに十分な大きさである場合、go test -shortを使用するための簡単なディレクティブを持つことができるMakefileを使用している可能性があります。または、それをREADME.mdファイルに入れて、その日に呼び出します。

32
eduncan911

私は最近同じ解決策を見つけようとしていました。これらは私の基準でした:

  • ソリューションは普遍的でなければなりません
  • 統合テスト用の個別のパッケージはありません
  • 分離が完了するはずです(統合テストを実行できるはずですonly
  • 統合テスト用の特別な命名規則はありません
  • 追加のツールなしでうまく機能するはずです

前述のソリューション(カスタムフラグ、カスタムビルドタグ、環境変数)は上記のすべての基準を実際には満たしていないため、少し掘り下げて遊んだ後、このソリューションを思い付きました。

package main

import (
    "flag"
    "regexp"
    "testing"
)

func TestIntegration(t *testing.T) {
    if m := flag.Lookup("test.run").Value.String(); m == "" || !regexp.MustCompile(m).MatchString(t.Name()) {
        t.Skip("skipping as execution was not requested explicitly using go test -run")
    }

    t.Parallel()

    t.Run("HelloWorld", testHelloWorld)
    t.Run("SayHello", testSayHello)
}

実装は簡単で最小限です。テストには簡単な規則が必要ですが、エラーが発生しにくいです。さらなる改善は、コードをヘルパー関数にエクスポートすることです。

使用法

プロジェクト内のすべてのパッケージでのみ統合テストを実行します。

go test -v ./... -run ^TestIntegration$

すべてのテストを実行します(regularおよび統合):

go test -v ./... -run .\*

regularテストのみを実行します:

go test -v ./...

このソリューションは、ツールを使用しなくても正常に機能しますが、Makefileまたは一部のエイリアスを使用するとユーザーが簡単になります。また、Goテストの実行をサポートするIDE=)に簡単に統合することもできます。

完全な例はここにあります: https://github.com/sagikazarmark/modern-go-application

2
mark.sagikazar