Goのテストパッケージを使用してテストセットアップを行う方法


111

テストパッケージを使用するときに、すべてのテストのステージを設定する全体的なテストセットアップ処理を実行するにはどうすればよいですか?

Nunitの例として、[SetUp]属性があります。

[TestFixture]
public class SuccessTests
{
  [SetUp] public void Init()
  { /* Load test data */ }
}

回答:


158

Go 1.4以降では、セットアップ/ティアダウンを実装できます(各テストの前後に関数をコピーする必要はありません)。ドキュメントは概説され、ここメインセクション:

TestMainはメインのゴルーチンで実行され、m.Runの呼び出しの前後に必要なセットアップとティアダウンを実行できます。次に、m.Runの結果でos.Exitを呼び出す必要があります

これは、テストに関数が含まれている場合、テストfunc TestMain(m *testing.M)を実行する代わりにこの関数が呼び出されることを意味することを理解するのに少し時間がかかりました。この関数では、テストの実行方法を定義できます。たとえば、グローバルセットアップとティアダウンを実装できます。

func TestMain(m *testing.M) {
    setup()
    code := m.Run() 
    shutdown()
    os.Exit(code)
}

他のいくつかの例がここにあります

最新のリリースでGoのテストフレームワークに追加されたTestMain機能は、いくつかのテストユースケースのためのシンプルなソリューションです。TestMainは、セットアップとシャットダウンの実行、テスト環境の制御、子プロセスでの異なるコードの実行、またはテストコードによってリークされたリソースのチェックを行うためのグローバルフックを提供します。ほとんどのパッケージはTestMainを必要としませんが、必要な場合は歓迎すべき追加です。


17
TestMainはパッケージに含まれているため、それほど役に立ちません。私は見つけるのサブテストは、より複雑な目的のために優れています。
イナンクムス

3
グローバル変数を使用せずに、セットアップ関数からテストにコンテキストを渡すにはどうすればよいですか?たとえば、mySetupFunction()が(一意のランダムな名前で)テストを実行するための一時ディレクトリを作成する場合、テストはディレクトリの名前をどのようにして知るのですか?このコンテキストを設定する場所がなければなりませんか?
Lqueryvg 2017

1
これは、テストの前後のフックを処理する公式の方法のようです。公式ドキュメントについては、golang.org
/ pkg / testing /#hdr-Mainを

4
@InancGumuslstat $GOROOT/subtests: no such file or directory
030

1
'code:= m.Run()'は他のTestFunctionsを実行するものであることに注意してください!
Alex Punnen

49

これは、init()関数を_test.goファイルに入れることで実現できます。これはinit()関数の前に実行されます。

// package_test.go
package main

func init() {
     /* load test data */
}

_test.init()は、パッケージのinit()関数の前に呼び出されます。


2
あなたはあなた自身の質問に答えているので、これはおそらくあなた自身のユースケースを満たしますが、これはあなたが質問に含めたNUnitの例と同等ではありません。
James Henstridge、2014年

@jamesさん、問題に答える方法について1つの考えを示しました。他の考えは、あなたを含め、すでにいくつかの優れた洞察を提供しています。アプローチを調整するために外部の影響を取得するのに役立ちます。ありがとう。
miltonb 2014年

2
けっこうだ。この回答で示したのは、[TestFixtureSetUp]代わりにNUnitの属性を使用することに多少近いものです。
James Henstridge、2014年

2
分解部分は含まれていません
Taras Matsyk 2018

7
テストファイルがmain関数と同じパッケージにある場合、これは良い解決策ではありません。
MouseWanted

28

単体テストする単純な関数があるとします。

package math

func Sum(a, b int) int {
    return a + b
}

ティアダウン関数を返すセットアップ関数でテストできます。また、setup()を呼び出した後、teardown()に遅延呼び出しを行うことができます。

package math

import "testing"

func setupTestCase(t *testing.T) func(t *testing.T) {
    t.Log("setup test case")
    return func(t *testing.T) {
        t.Log("teardown test case")
    }
}

func setupSubTest(t *testing.T) func(t *testing.T) {
    t.Log("setup sub test")
    return func(t *testing.T) {
        t.Log("teardown sub test")
    }
}

func TestAddition(t *testing.T) {
    cases := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"add", 2, 2, 4},
        {"minus", 0, -2, -2},
        {"zero", 0, 0, 0},
    }

    teardownTestCase := setupTestCase(t)
    defer teardownTestCase(t)

    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            teardownSubTest := setupSubTest(t)
            defer teardownSubTest(t)

            result := Sum(tc.a, tc.b)
            if result != tc.expected {
                t.Fatalf("expected sum %v, but got %v", tc.expected, result)
            }
        })
    }
}

Goテストツールは、シェルコンソールにログステートメントを報告します。

% go test -v
=== RUN   TestAddition
=== RUN   TestAddition/add
=== RUN   TestAddition/minus
=== RUN   TestAddition/zero
--- PASS: TestAddition (0.00s)
    math_test.go:6: setup test case
    --- PASS: TestAddition/add (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/minus (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/zero (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    math_test.go:8: teardown test case
PASS
ok      github.com/kare/go-unit-test-setup-teardown 0.010s
% 

このアプローチでは、いくつかの追加パラメーターをセットアップ/ティアダウンに渡すことができます。


2
これは本当にシンプルですが効果的なトリックです。Go構文の優れた使用法。
miltonb 2016年

1
ええ、でもそれはネストネスを増やします(JavaScriptの一種の運命のピラミッド)。また、テストは、外部テストのようにスイートによって自動的に実行されることはありません。
イナンクムス2017

12

通常、goのテストは他の言語と同じスタイルで書かれていません。多くの場合、テスト関数は比較的少ないですが、それぞれにテーブル駆動のテストケースのセットが含まれています。Goチームの1人が作成したこの記事を参照してください

テーブル駆動テストでは、テーブルで指定された個々のテストケースを実行するループの前にセットアップコードを置き、その後にクリーンアップコードを置くだけです。

テスト関数間で共有セットアップコードがまだある場合は、共有セットアップコードを関数に抽出し、sync.Onceそれを正確に1回実行することが重要な場合に使用できます(または別の答えが示唆するようにを使用しますinit()が、これにはセットアップの欠点がありますテストケースが実行されない場合でも実行されます(おそらく、を使用してテストケースを制限したためですgo test -run <regexp>)。

異なるテスト間で正確に一度だけ実行されるセットアップを共有する必要があると思うなら、本当にそれが必要かどうか、そしてテーブル駆動のテストの方が優れているかどうかについて考えるべきです。


6
フラグパーサーや、数値をチャーンするアルゴリズムなど、ささいなことをテストする場合に最適です。しかし、同様のボイラープレートコードをすべて必要とするさまざまな機能をテストしようとしても、実際には役に立ちません。私はテスト関数を配列で定義してそれらを反復することができると思いますが、実際には、適切なテストスイートの形式でテストフレームワーク自体に組み込む必要がある単純なループほどテーブル駆動ではありません。セットアップ/ティアダウン機能付き)
iamtheddrman 2017年

9

Goテストフレームワークには、NUnitのSetUp属性(スイートの各テストの前に呼び出される関数をマークする)に相当するものはありません。ただし、いくつかのオプションがあります。

  1. SetUp必要な各テストから関数を呼び出すだけです。

  2. xUnitパラダイムと概念を実装するGoのテストフレームワークの拡張機能を使用します。3つの強力なオプションが思い浮かびます:

これらの各ライブラリは、テストを他のxUnitフレームワークと同様のスイート/フィクスチャに編成することを奨励し、各Test*メソッドの前にスイート/フィクスチャタイプのセットアップメソッドを呼び出します。


0

恥知らずなプラグイン、私はこの問題を正確に解決するためにhttps://github.com/houqp/gtestを作成しました。

ここに簡単な例があります:

import (
  "strings"
  "testing"
  "github.com/houqp/gtest"
)

type SampleTests struct{}

// Setup and Teardown are invoked per test group run
func (s *SampleTests) Setup(t *testing.T)      {}
func (s *SampleTests) Teardown(t *testing.T)   {}
// BeforeEach and AfterEach are invoked per test run
func (s *SampleTests) BeforeEach(t *testing.T) {}
func (s *SampleTests) AfterEach(t *testing.T)  {}

func (s *SampleTests) SubTestCompare(t *testing.T) {
  if 1 != 1 {
    t.FailNow()
  }
}

func (s *SampleTests) SubTestCheckPrefix(t *testing.T) {
  if !strings.HasPrefix("abc", "ab") {
    t.FailNow()
  }
}

func TestSampleTests(t *testing.T) {
  gtest.RunSubTests(t, &SampleTests{})
}

パッケージ内に任意のテストグループを作成し、それぞれに異なるセットアップ/ティアダウンルーチンのセットを使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.