欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang使用Docker進(jìn)行集成測試的示例詳解

 更新時(shí)間:2023年07月27日 08:58:14   作者:江湖十年  
集成測試需要解決外部依賴問題,如?MySQL、Redis、網(wǎng)絡(luò)等依賴,本文就來聊聊?Go?程序如何使用?Docker?來解決集成測試中外部依賴問題吧

實(shí)踐表明,有時(shí)程序中某個(gè)模塊雖然可以單獨(dú)工作,但是并不能保證多個(gè)模塊組裝起來也可以同時(shí)工作,于是就有了集成測試。

集成測試需要解決外部依賴問題,如 MySQL、Redis、網(wǎng)絡(luò)等依賴,解決這些外部依賴問題最佳實(shí)踐則是使用 Docker,本文就來聊聊 Go 程序如何使用 Docker 來解決集成測試中外部依賴問題。

登錄程序示例

在 Web 開發(fā)中,登錄需求是一個(gè)較為常見的功能。所以,本文就以登錄程序?yàn)槔?,講解使用 Docker 啟動(dòng) Redis 進(jìn)行集成測試。

登錄程序如下:

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
}

可以通過如下方式獲取 Redis 客戶端對象 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)行集成測試

要想對 Login 函數(shù)進(jìn)行集成測試,就需要解決 Reids 外部依賴問題。

在 Go 程序中,我們可以使用 testcontainers-go 這個(gè)包來解決,它可以讓我們很方便的在 Docker 中啟動(dòng) Reids 服務(wù)。

安裝 testcontainers-go

$ go get github.com/testcontainers/testcontainers-go

我們可以在測試代碼開始執(zhí)行之前啟動(dòng) Docker 容器來運(yùn)行 Redis 服務(wù),然后執(zhí)行測試代碼,最后測試代碼執(zhí)行完成后再停止并刪除 Docker 容器。

可以定義一個(gè) setup 函數(shù)用來準(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 容器非常簡單,我們指定了 Docker 容器鏡像為 redis:6.0.20-alpine,映射端口為 6379/tcp

Redis 容器啟動(dòng)后將實(shí)例化的 Redis 客戶端保存到全局變量 rdbClient 中,方便在測試函數(shù)中使用,setup 函數(shù)最終返回一個(gè) teardown 函數(shù)可以清理容器。

定義 TestMain 函數(shù)如下,作為測試程序的入口:

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

為了測試 Login 函數(shù),我們需要在 Reids 中準(zhǔn)備一些測試數(shù)據(jù),因?yàn)?Login 函數(shù)內(nèi)部需要查詢 Reids 中的驗(yàn)證碼,所以可以定義一個(gè) setupLogin 函數(shù)來實(shí)現(xiàn):

func setupLogin(tb testing.TB) func(tb testing.TB) {
	// 準(zhǔn)備測試數(shù)據(jù)
	err := SetSmsCaptchaToRedis(context.Background(), rdbClient, "18900001111", "123456")
	assert.NoError(tb, err)
	// 清理測試數(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ù)用來清理 Redis 中的測試數(shù)據(jù),防止當(dāng)有多個(gè)測試函數(shù)時(shí)互相影響。

現(xiàn)在可以編寫 Login 函數(shù)的測試代碼了:

func TestLogin(t *testing.T) {
	teardownLogin := setupLogin(t)
	defer teardownLogin(t)
	// 測試登錄成功情況
	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ù)非常簡單,這得益于前期的準(zhǔn)備工作做的非常全面。

使用 go test 來執(zhí)行測試函數(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

測試通過。

總結(jié)

我們使用 testcontainers-go 包實(shí)現(xiàn)了在 Go 程序中啟動(dòng)一個(gè) Docker 容器,以此解決了集成測試中依賴外部 Redis 問題。

可以發(fā)現(xiàn),Docker 非常適合集成測試,使用 Dokcer 來輔助集成測試是 Go 應(yīng)用程序集成測試的最佳實(shí)踐。而完善的集成測試,可以確保應(yīng)用程序的可靠性、可擴(kuò)展性和可維護(hù)性。

以上就是Golang使用Docker進(jìn)行集成測試的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Go Docker集成測試的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go?Interface接口初學(xué)者手冊

    Go?Interface接口初學(xué)者手冊

    這篇文章主要為大家介紹了Go?Interface接口的基礎(chǔ)用法實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go每日一庫之dateparse處理時(shí)間

    Go每日一庫之dateparse處理時(shí)間

    不管什么時(shí)候,處理時(shí)間總是讓人頭疼的一件事情。今天要介紹的dateparse實(shí)現(xiàn)解析日期時(shí)間格式的字符串。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • GO語言如何手動(dòng)處理TCP粘包詳解

    GO語言如何手動(dòng)處理TCP粘包詳解

    最近在用golang開發(fā)人工客服系統(tǒng)的時(shí)候碰到了粘包問題,那么什么是粘包呢?下面這篇文章就來給大家介紹了關(guān)于GO語言如何手動(dòng)處理TCP粘包的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒。
    2017-12-12
  • Golang中設(shè)置全局變量并在其他文件中使用

    Golang中設(shè)置全局變量并在其他文件中使用

    全局變量是被整個(gè)程序都可見的變量,通常用于存儲(chǔ)程序中需要共享的數(shù)據(jù),本文就來介紹一下Golang中設(shè)置全局變量并在其他文件中使用的方法,感興趣的可以了解一下
    2024-01-01
  • Go疑難雜癥講解之為什么nil不等于nil

    Go疑難雜癥講解之為什么nil不等于nil

    在日常開發(fā)中,可能一不小心就會(huì)掉進(jìn)?Go?語言的某些陷阱里,而本文要介紹的?nil?≠?nil?問題,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-10-10
  • Go語言深度拷貝工具deepcopy的使用教程

    Go語言深度拷貝工具deepcopy的使用教程

    今天給大家推薦的工具是deepcopy,一個(gè)可以對指針、接口、切片、結(jié)構(gòu)體、Map都能進(jìn)行深拷貝的工具,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)學(xué)習(xí)
    2022-09-09
  • Go語言init函數(shù)詳解

    Go語言init函數(shù)詳解

    今天小編就為大家分享一篇關(guān)于Go語言init函數(shù)詳解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Golang pipe在不同場景下遠(yuǎn)程交互

    Golang pipe在不同場景下遠(yuǎn)程交互

    這篇文章主要介紹了Golang pipe在不同場景下遠(yuǎn)程交互,pipe實(shí)現(xiàn)從一個(gè)進(jìn)程重定向至另一個(gè)進(jìn)程,它是雙向數(shù)據(jù)通道,用于實(shí)現(xiàn)進(jìn)行間通信
    2023-03-03
  • golang 實(shí)現(xiàn)interface{}轉(zhuǎn)其他類型操作

    golang 實(shí)現(xiàn)interface{}轉(zhuǎn)其他類型操作

    這篇文章主要介紹了golang 實(shí)現(xiàn)interface{}轉(zhuǎn)其他類型操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 詳解Golang中NewTimer計(jì)時(shí)器的底層實(shí)現(xiàn)原理

    詳解Golang中NewTimer計(jì)時(shí)器的底層實(shí)現(xiàn)原理

    本文將主要介紹一下Go語言中的NewTimer,首先展示基于NewTimer創(chuàng)建的定時(shí)器來實(shí)現(xiàn)超時(shí)控制。接著通過一系列問題的跟進(jìn),展示了NewTimer的底層實(shí)現(xiàn)原理,需要的可以參考一下
    2023-05-05

最新評論