2016-06-01 47 views
1

我剛剛開始使用Golang並編寫我的第一個測試套件。我有一個Rails的背景,它對測試工具(Rspec,Cucumber等)有很好的支持,所以我用類似的思維方式來接近我的golang測試(不知道這是對還是錯的東西做)Golang測試中可重複使用的組件和固件

我有讀取來自一個users表中的記錄中的Postgres和存儲它們的陣列的User數據模型(基本上是結構)。 (本質上是ActiveRecord在Rails世界中的一個非常簡單的版本)

我想編寫一個測試,檢查例程是否正確地從數據庫讀取並構建模型。

  1. 幾乎在每一個測試套件,我將連接到數據庫,所以我有一個名爲establish_db_connection幫手。我可以在哪裏放置它,以便它可以集中在我的所有測試中?

  2. 大廈關閉#1 - 是否有相當於before塊或某種設置/拆卸方法,我可以在每次測試之前建立連接?

  3. 最後,我該如何處理夾具?在每次測試之前,我會調用一個clear_db函數來重置所有表並插入一些靜態數據行。我很想擺脫固定裝置,並使用工廠根據需要構建數據(非常類似於Rails中的FactoryGirl),但不確定Golang的普遍性。

  4. 是內置的go test框架最好的方法,還是有更好的選擇?

+0

上派的回答跟進,你可以做一個'testfixtures'包,如果你需要使用跨多個軟件包的測試 – Plato

回答

1
  1. 轉到基於強大的包管理,這意味着一個命名空間作爲一個單一的文件處理。如果在一個測試包中使用establish_db_connection,則它可以以小寫字母開頭,表示一個私有實例,並在測試文件中使用它與被測試代碼具有相同的包(請注意,Go中的命名約定爲establishDBConnection)。 然而,在大多數情況下,如在data/sql中,您將需要獲取一次數據庫連接,並保持這種狀態直到測試完成(更像工廠和注入模式)。

  2. 標準testing包中沒有。如果你喜歡BDD,Goconvey使用示波器來定義燈具和拆卸功能reset

  3. 您可以在測試中使用工廠和依賴項注入。我認爲這很好用。

  4. 一些包括GoconveyGinkgoTestify他們都有自己的優點和缺點。前兩種情況通常會導致嵌套式作用域過多,但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'。你可以根據需要創建許多'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

相關問題