Golang使用Docker進(jìn)行集成測(cè)試的示例詳解
實(shí)踐表明,有時(shí)程序中某個(gè)模塊雖然可以單獨(dú)工作,但是并不能保證多個(gè)模塊組裝起來(lái)也可以同時(shí)工作,于是就有了集成測(cè)試。
集成測(cè)試需要解決外部依賴問(wèn)題,如 MySQL、Redis、網(wǎng)絡(luò)等依賴,解決這些外部依賴問(wèn)題最佳實(shí)踐則是使用 Docker,本文就來(lái)聊聊 Go 程序如何使用 Docker 來(lái)解決集成測(cè)試中外部依賴問(wèn)題。
登錄程序示例
在 Web 開(kāi)發(fā)中,登錄需求是一個(gè)較為常見(jiàn)的功能。所以,本文就以登錄程序?yàn)槔v解使用 Docker 啟動(dòng) Redis 進(jìn)行集成測(cè)試。
登錄程序如下:
func Login(mobile, smsCode string, rdb *redis.Client) (string, error) {
ctx := context.Background()
// 查找驗(yàn)證碼
captcha, err := GetSmsCaptchaFromRedis(ctx, rdb, mobile)
if err != nil {
if err == redis.Nil {
return "", fmt.Errorf("invalid sms code or expired")
}
return "", err
}
if captcha != smsCode {
return "", fmt.Errorf("invalid sms code")
}
token, _ := GenerateToken(32)
err = SetAuthTokenToRedis(ctx, rdb, token, mobile)
if err != nil {
return "", err
}
return token, nil
}可以通過(guò)如下方式獲取 Redis 客戶端對(duì)象 rdb:
import "github.com/redis/go-redis/v9"
func NewRedisClient() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
}生成隨機(jī) token 的函數(shù)定義如下:
var GenerateToken = func(length int) (string, error) {
token := make([]byte, length)
_, err := rand.Read(token)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(token)[:length], nil
}本程序提供了如下幾個(gè)操作 Reids 的函數(shù):
var (
smsCaptchaExpire = 5 * time.Minute
smsCaptchaKeyPrefix = "sms:captcha:%s"
authTokenExpire = 24 * time.Hour
authTokenKeyPrefix = "auth:token:%s"
)
func SetSmsCaptchaToRedis(ctx context.Context, redis *redis.Client, mobile, captcha string) error {
key := fmt.Sprintf(smsCaptchaKeyPrefix, mobile)
return redis.Set(ctx, key, captcha, smsCaptchaExpire).Err()
}
func GetSmsCaptchaFromRedis(ctx context.Context, redis *redis.Client, mobile string) (string, error) {
key := fmt.Sprintf(smsCaptchaKeyPrefix, mobile)
return redis.Get(ctx, key).Result()
}
func DeleteSmsCaptchaFromRedis(ctx context.Context, redis *redis.Client, mobile string) error {
key := fmt.Sprintf(smsCaptchaKeyPrefix, mobile)
return redis.Del(ctx, key).Err()
}
func SetAuthTokenToRedis(ctx context.Context, redis *redis.Client, token, mobile string) error {
key := fmt.Sprintf(authTokenKeyPrefix, token)
return redis.Set(ctx, key, mobile, authTokenExpire).Err()
}
func GetAuthTokenFromRedis(ctx context.Context, redis *redis.Client, token string) (string, error) {
key := fmt.Sprintf(authTokenKeyPrefix, token)
return redis.Get(ctx, key).Result()
}
func DeleteAuthTokenFromRedis(ctx context.Context, redis *redis.Client, token string) error {
key := fmt.Sprintf(authTokenKeyPrefix, token)
return redis.Del(ctx, key).Err()
}Login 函數(shù)用法如下:
func main() {
rdb := NewRedisClient()
token, err := Login("13800001111", "123456", rdb)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(token)
}使用 Docker 進(jìn)行集成測(cè)試
要想對(duì) Login 函數(shù)進(jìn)行集成測(cè)試,就需要解決 Reids 外部依賴問(wèn)題。
在 Go 程序中,我們可以使用 testcontainers-go 這個(gè)包來(lái)解決,它可以讓我們很方便的在 Docker 中啟動(dòng) Reids 服務(wù)。
安裝 testcontainers-go:
$ go get github.com/testcontainers/testcontainers-go
我們可以在測(cè)試代碼開(kāi)始執(zhí)行之前啟動(dòng) Docker 容器來(lái)運(yùn)行 Redis 服務(wù),然后執(zhí)行測(cè)試代碼,最后測(cè)試代碼執(zhí)行完成后再停止并刪除 Docker 容器。
可以定義一個(gè) setup 函數(shù)用來(lái)準(zhǔn)備 Docker 容器:
var rdbClient *redis.Client
func setup() func() {
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "redis:6.0.20-alpine",
ExposedPorts: []string{"6379/tcp"},
WaitingFor: wait.ForLog("Ready to accept connections"),
}
redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
panic(fmt.Sprintf("failed to start container: %s", err.Error()))
}
endpoint, err := redisC.Endpoint(ctx, "")
if err != nil {
panic(fmt.Sprintf("failed to get endpoint: %s", err.Error()))
}
rdbClient = redis.NewClient(&redis.Options{
Addr: endpoint,
})
// 清理 Redis 容器
return func() {
if err := redisC.Terminate(ctx); err != nil {
panic(fmt.Sprintf("failed to terminate container: %s", err.Error()))
}
}
}可以發(fā)現(xiàn)使用 testcontainers-go 啟動(dòng)一個(gè) Redis 容器非常簡(jiǎn)單,我們指定了 Docker 容器鏡像為 redis:6.0.20-alpine,映射端口為 6379/tcp。
Redis 容器啟動(dòng)后將實(shí)例化的 Redis 客戶端保存到全局變量 rdbClient 中,方便在測(cè)試函數(shù)中使用,setup 函數(shù)最終返回一個(gè) teardown 函數(shù)可以清理容器。
定義 TestMain 函數(shù)如下,作為測(cè)試程序的入口:
func TestMain(m *testing.M) {
teardown := setup()
code := m.Run()
teardown()
os.Exit(code)
}為了測(cè)試 Login 函數(shù),我們需要在 Reids 中準(zhǔn)備一些測(cè)試數(shù)據(jù),因?yàn)?Login 函數(shù)內(nèi)部需要查詢 Reids 中的驗(yàn)證碼,所以可以定義一個(gè) setupLogin 函數(shù)來(lái)實(shí)現(xiàn):
func setupLogin(tb testing.TB) func(tb testing.TB) {
// 準(zhǔn)備測(cè)試數(shù)據(jù)
err := SetSmsCaptchaToRedis(context.Background(), rdbClient, "18900001111", "123456")
assert.NoError(tb, err)
// 清理測(cè)試數(shù)據(jù)
return func(tb testing.TB) {
err := DeleteSmsCaptchaFromRedis(context.Background(), rdbClient, "18900001111")
assert.NoError(tb, err)
err = DeleteAuthTokenFromRedis(context.Background(), rdbClient, "token")
assert.NoError(tb, err)
}
}setupLogin 函數(shù)返回 teardownLogin 函數(shù)用來(lái)清理 Redis 中的測(cè)試數(shù)據(jù),防止當(dāng)有多個(gè)測(cè)試函數(shù)時(shí)互相影響。
現(xiàn)在可以編寫 Login 函數(shù)的測(cè)試代碼了:
func TestLogin(t *testing.T) {
teardownLogin := setupLogin(t)
defer teardownLogin(t)
// 測(cè)試登錄成功情況
token, err := Login("18900001111", "123456", rdbClient)
assert.NoError(t, err)
assert.Equal(t, "token", token)
// 檢查 Redis 中是否存在 token
mobile, err := GetAuthTokenFromRedis(context.Background(), rdbClient, "token")
assert.NoError(t, err)
assert.Equal(t, "18900001111", mobile)
}TestLogin 函數(shù)非常簡(jiǎn)單,這得益于前期的準(zhǔn)備工作做的非常全面。
使用 go test 來(lái)執(zhí)行測(cè)試函數(shù):
$ go test -v
2023/07/26 20:48:12 github.com/testcontainers/testcontainers-go - Connected to docker:
Server Version: 20.10.21
API Version: 1.41
Operating System: Docker Desktop
Total Memory: 7851 MB
2023/07/26 20:48:12 ?? Creating container for image docker.io/testcontainers/ryuk:0.5.1
2023/07/26 20:48:12 ? Container created: a261dc723001
2023/07/26 20:48:12 ?? Starting container: a261dc723001
2023/07/26 20:48:12 ? Container started: a261dc723001
2023/07/26 20:48:12 ?? Waiting for container id a261dc723001 image: docker.io/testcontainers/ryuk:0.5.1. Waiting for: &{Port:8080/tcp timeout:<nil> PollInterval:100ms}
2023/07/26 20:48:13 ?? Creating container for image redis:6.0.20-alpine
2023/07/26 20:48:13 ? Container created: 6420ead815a0
2023/07/26 20:48:13 ?? Starting container: 6420ead815a0
2023/07/26 20:48:13 ? Container started: 6420ead815a0
2023/07/26 20:48:13 ?? Waiting for container id 6420ead815a0 image: redis:6.0.20-alpine. Waiting for: &{timeout:<nil> Log:Ready to accept connections Occurrence:1 PollInterval:100ms}
=== RUN TestLogin
--- PASS: TestLogin (0.01s)
PASS
2023/07/26 20:48:13 ?? Terminating container: 6420ead815a0
2023/07/26 20:48:13 ?? Container terminated: 6420ead815a0
ok github.com/jianghushinian/test/db/redis 1.630s
測(cè)試通過(guò)。
總結(jié)
我們使用 testcontainers-go 包實(shí)現(xiàn)了在 Go 程序中啟動(dòng)一個(gè) Docker 容器,以此解決了集成測(cè)試中依賴外部 Redis 問(wèn)題。
可以發(fā)現(xiàn),Docker 非常適合集成測(cè)試,使用 Dokcer 來(lái)輔助集成測(cè)試是 Go 應(yīng)用程序集成測(cè)試的最佳實(shí)踐。而完善的集成測(cè)試,可以確保應(yīng)用程序的可靠性、可擴(kuò)展性和可維護(hù)性。
以上就是Golang使用Docker進(jìn)行集成測(cè)試的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Go Docker集成測(cè)試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go語(yǔ)言實(shí)現(xiàn)兩個(gè)協(xié)程交替打印
這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)兩個(gè)協(xié)程交替打印,文章主要分享了兩種方法使用兩個(gè)channel和使用一個(gè)channel,內(nèi)容介紹詳細(xì)具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-03-03
go語(yǔ)言代碼生成器code?generator使用示例介紹
這篇文章主要為大家介紹了go語(yǔ)言代碼生成器code?generator的使用簡(jiǎn)單介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
go語(yǔ)言beego框架分頁(yè)器操作及接口頻率限制示例
這篇文章主要為大家介紹了go語(yǔ)言beego框架分頁(yè)器操作使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
Go語(yǔ)言數(shù)據(jù)類型簡(jiǎn)單介紹
這篇文章主要介紹了Go語(yǔ)言數(shù)據(jù)類型簡(jiǎn)單介紹的相關(guān)資料,需要的朋友可以參考下2023-08-08
一文帶你搞懂Golang依賴注入的設(shè)計(jì)與實(shí)現(xiàn)
在現(xiàn)代的 web 框架里面,基本都有實(shí)現(xiàn)了依賴注入的功能,可以讓我們很方便地對(duì)應(yīng)用的依賴進(jìn)行管理。今天我們來(lái)看看 go 里面實(shí)現(xiàn)依賴注入的一種方式,感興趣的可以了解一下2023-01-01
golang通用的grpc?http基礎(chǔ)開(kāi)發(fā)框架使用快速入門
這篇文章主要為大家介紹了golang通用的grpc?http基礎(chǔ)開(kāi)發(fā)框架使用快速入門詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟
本文主要介紹了gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02

