2016-06-01 24 views
1

私はGolangを使いはじめ、最初のテストスイートを作成しています。Golangテストの再利用可能なコンポーネントとフィクスチャ

私は、テストツール(Rspec、Cucumberなど)のための素晴らしいサポートを持っているRailsの背景を持っています。私は同様の考え方でゴーランテストに近づいています。

私はUserというデータモデル(基本的に構造体)を持っています。これは、postgresのusersテーブルからレコードを読み込み、その配列を格納します。 (基本的にはActiveRecordはRailsの世界で何をするかの本当に簡単バージョン)

私はルーチンが正しくDBから読み込んで、モデルを構築するかどうかを確認するテストを書きたいと思います。私はDBに接続されますほぼすべてのテストスイートで

  1. ので、私はestablish_db_connectionという名前のヘルパーを持っています。すべてのテストで一元的に利用できるように、この場所をどこに置くことができますか?

  2. ビルドオフ#1 - すべてのテストの前に接続を確立することができるbeforeブロックまたはセットアップ/ティアダウンメソッドの同等物はありますか?

  3. 最後に、どのように私は器具を扱うのですか?各テストの直前に、私はclear_db関数を呼び出してすべてのテーブルをリセットし、いくつかの静的データ行を挿入します。私はフィクスチャーから離れ、必要に応じて工場を使用してデータを構築したい(RailsのFactoryGirlに非常に似ています)が、Golangの一般的な方法はわかりません。

  4. 内蔵go test枠組み最善のアプローチです、またはより良い代替手段がありますか?

+0

パイの答えのフォローアップは、複数のパッケージのテスト全体でヘルパーを使用する必要がある場合、あなたは 'testfixtures'パッケージを作ることができます – Plato

答えて

1
  1. ゴーは、名前空間が1つのファイルとして扱われる意味、強力なパッケージ管理に基づいています。 establish_db_connectionが単一のテストパッケージ内で使用されている場合は、プライベートインスタンスを表す小文字で始まり、テストするコードと同じパッケージのテストファイルで使用できます(Goの命名規則はestablishDBConnectionです)。 しかし、ほとんどの場合、data/sqlのように、一度DB接続を取得し、テストが完了するまで(工場と注入パターンのように)、DB接続を保持したいと思うでしょう。

  2. 標準testingパッケージで何もありません。 BDDが好きな場合は、Goconveyはスコープを使用して治具を定義し、ティアダウンにはreset関数を使用します。

  3. あなたのテストで、工場や依存性の注入を使用することができます。私はそれがかなり熟語だと思う。

  4. いくつかは、Goconvey,GinkgoおよびTestifyを含み、それらはすべて自分の賛否両論があります。最初の2つはネストされたスコープが多すぎることが多いですが、Goconveyにはブラウザベースのリアルタイムテストサーバーがあり、Go標準テストで使用できます。

行くにはグローバル変数/関数がありませんので、あなたはパッケージを渡る機能をインポートし、クロスパッケージのテストを扱うときに巡回輸入を避けることを助けるためにinterface-delegate patternでプロジェクトを設計することがあります。

備品の
mypackage 

type DBOptions struct { 
     Name, Credentials string 
} 

func aFunc(db *sql.DB) error { 
     // do something 
     return nil 
} 

func bFunc(db *sql.DB) int, error { 
     // do something 
     return 0, nil 
} 

func establishConn(opts *DBOptions) (*sql.DB, error) { 
     db, err := sql.Open(opts.Name, opts.Credentials) 
     if err != nil { 
       return nil, err 
     } 
     return db, nil 
} 

func destroyConn(conn *sql.DB) { 
     conn.Close() 
} 

// test file 
mypackage 

import "testing" 

var myOpt = &DBOptions{ 
     Name: "mysql", 
     Credentials: "user:[email protected](127.0.0.1:3306)/hello", 
} 

var conn, _ = establishConn(myOpt) 

func TestAFunc(t *testing.T) { 
     err := aFunc(conn) 

     if err != nil { 
       t.Error(err) 
     } 
} 

func TestBFunc(t *testing.T) { 
     err := aFunc(conn) 

     if err != nil { 
       t.Error(err) 
     } 
} 

// use `conn` in other tests ... 

destroyConn(conn) 
+0

ありがとう、私は詳細な説明に感謝!そうです、それはすべてのそれらのテスト図書館が彼ら自身の賛否両論を持っているように見えます。私はPostgresのORMを検索するときに同じ問題に遭遇しました - それはいくつかありますが、残念なことに本当に堅牢な標準として現れたものはありません。あなたの例に関しては、テストファイルの 'myOpt'と' conn'はどの関数でも定義されていませんので、定義された順序で一度だけ実行され、すべてのテストで利用できると思いますか? – user2490003

+0

はい、 'conn'は一度作成され、' destroyConn'が呼び出されるまで利用可能です。あなたはmockを含めて、あなたが望むように多くの 'conn'を作成することができ、テストの際にそれらを入れ替えることができます。 – PieOhPah

1

は:あなたのテストケース内の関数を渡して考えてみます。

package main 

import "testing" 

type testcase struct { 
    scenario string 
    before func(string) 
    after  func() 
    input  string 
    expOutput string 
} 

var state = "" 

func setup(s string) { 
    state = s 
} 

func nilSetup(s string) {} 

func reset() { 
    state = "" 
} 

func execute(s string) string { 
    return state 
} 

func TestSetupTeardown(t *testing.T) { 
    tcs := []testcase{ 
     { 
      scenario: "blank output when initial state is wrong", 
      before: nilSetup, 
      after:  reset, 
      input:  "foo", 
      expOutput: "", 
     }, 
     { 
      scenario: "correct output when initial state is right", 
      before: setup, 
      after:  reset, 
      input:  "foo", 
      expOutput: "foo", 
     }, 
    } 

    for _, tc := range tcs { 
     tc.before(tc.input) 
     if out := execute(tc.input); out != tc.expOutput { 
      t.Fatal(tc.scenario) 
     } 
     tc.after() 
    } 
} 
+0

任意の考え@pieohpah? – Plato

関連する問題