Go語言:如何使用testcontainers構建數據訪問層的集成測試?
如何確保數據訪問層中的SQL語句編寫正確?即使在數據庫客戶端中手動測試通過的SQL語句,放到程序代碼中也不能保證一定能正常工作。
問題示例
讓我們看一個查詢tier=2的客戶記錄的SQL語句示例:
SELECT id, name, tier, created_at, updated_at
FROM customer
WHERE tier = 2
然而,當這條SQL語句被放入程序代碼中時可能會出現問題:
// GO
// connect to database
db, err := sql.Open("mysql", <<database connection string>>)
// execute query
rows, err := db.Query("SELECT id, name, tier," + "created_at, updated_at"+"FROM customer WHERE tier=?", tier)
你能發現問題所在嗎?是的,在update_at前的換行處缺少了一個空格。這是在將SQL語句放入程序代碼時常見的粗心錯誤。肉眼檢查可能不容易發現這個問題,而合適的自動化測試才是確保程序代碼按預期工作的可靠方式。
使用容器進行測試
啟動真實數據庫進行測試是最佳方法。得益于Docker容器的普及,自動化測試可以在容器中啟動數據庫進行測試,測試完成后直接銷毀。
在Java中,使用庫方法調用和JUnit可以輕松實現與數據庫的集成測試。那么在GO中是否也可以構建相同的自動化測試呢?
好消息是testcontainers庫也支持Go。本文將深入探討如何使用Go構建集成測試。
數據訪問層
為了實現關注點分離和便于維護,將SQL語句混入業務邏輯通常不是一個好主意。最佳實踐是將它們封裝在一個稱為數據訪問對象(DAO)的獨立組件中,該組件充當業務邏輯和數據庫之間的適配器。
使用MySQL測試容器
首先讓我們看看如何啟動測試容器的基本操作。
需要導入testcontainers庫的以下兩個包:
import (
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/mysql"
)
使用mysql.Run()并傳入以下參數來啟動MySQL容器:
- MySQL鏡像版本
- 數據庫名稱
- 用戶名和密碼
- 初始數據庫腳本
ctx := context.Background()
mysqlContainer, err := mysql.Run(ctx,
"mysql:8.0.36",
mysql.WithDatabase("example"),
mysql.WithUsername("appuser"),
mysql.WithPassword("passme"),
mysql.WithScripts(filepath.Join("testdata", "schema.sql")),
)
if err != nil {
log.Panicf("failed to start container: %s\n", err)
}
連接MySQL容器
要連接MySQL數據庫,需要導入Go的標準庫database/sql和MySQL驅動。為避免與testcontainer庫的MySQL模塊名稱沖突,給驅動包添加下劃線別名:
import (
_ "github.com/go-sql-driver/mysql"
"database/sql"
)
與Java類似,Go中的數據庫連接也是通過連接字符串實現。testcontainers庫提供了一個便捷函數mysqlContainer.ConnectionString(),它可以生成格式正確的連接字符串。
測試套件實現
首先,定義一個包含客戶DAO、MySQL容器和數據庫連接引用的測試套件結構體。suite.Suite提供了測試框架的元素,如用于管理測試狀態和支持測試日志的testing.T。
type CustomerDaoTestSuite struct {
suite.Suite
dao *dao.CustomerDao
mysqlContainer *mysql.MySQLContainer
db *sql.DB
}
生命周期函數
Testify框架提供了以下接口來實現測試生命周期:
- SetupSuite() - 在所有測試場景執行前調用一次(相當于JUnit中的@BeforeAll)
- SetupTest() - 每個測試場景前的設置函數(相當于JUnit中的@BeforeEach)
- TearDownTest() - 每個測試場景完成后的清理函數(相當于JUnit中的@AfterEach)
- TearDownSuite() - 在所有測試場景結束后調用一次(相當于JUnit中的@AfterAll)
最終思考
數據訪問層的集成測試至關重要。即使SQL語句在數據庫客戶端中運行完美,放入程序代碼中也不能保證正常工作。使用真實數據庫進行測試是確認組件正常運行的唯一方法。
借助testcontainers庫和Testify框架,集成測試可以自動啟動容器中的數據庫,并在完整的測試生命周期中將DAO連接到數據庫進行測試,就像Java中的JUnit一樣。該庫對開發人員友好,只需幾個函數調用就能輕松完成測試設置。