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

詳解Go語言單元測試中如何解決MySQL存儲依賴問題

 更新時間:2023年07月31日 09:19:01   作者:江湖十年  
MySQL?存儲就是一個非常常見的外部依賴,這篇文章主要來和大家一起探討在?Go?語言中編寫單元測試時,如何解決?MySQL?存儲依賴,需要的可以參考一下

在編寫單元測試的過程中,如果被測試代碼有外部依賴,為了便于測試,我們就要想辦法來解決這些外部依賴問題。在做 Web 開發(fā)時,MySQL 存儲就是一個非常常見的外部依賴,本文就來探討在 Go 語言中編寫單元測試時,如何解決 MySQL 存儲依賴。

HTTP 服務(wù)程序示例

假設(shè)我們有一個 HTTP 服務(wù)程序?qū)ν馓峁┓?wù),代碼如下:

main.go

package main
import (
	"encoding/json"
	"fmt"
	"gorm.io/gorm"
	"io"
	"net/http"
	"strconv"
	"github.com/julienschmidt/httprouter"
	"github.com/jianghushinian/blog-go-example/test/mysql/store"
)
func NewUserHandler(db *gorm.DB) *UserHandler {
	return &UserHandler{
		store: store.NewUserStore(db),
	}
}
type UserHandler struct {
	store store.UserStore
}
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	...
}
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	...
}
func setupRouter(handler *UserHandler) *httprouter.Router {
	router := httprouter.New()
	router.POST("/users", handler.CreateUser)
	router.GET("/users/:id", handler.GetUser)
	return router
}
func main() {
	mysqlDB, _ := store.NewMySQLDB("localhost", "3306", "user", "password", "test")
	handler := NewUserHandler(mysqlDB)
	router := setupRouter(handler)
	_ = http.ListenAndServe(":8000", router)
}

這個服務(wù)監(jiān)聽 8000 端口,分別提供了兩個 HTTP 接口:

POST /users 用來創(chuàng)建用戶。

GET /users/:id 用來獲取指定 ID 對應(yīng)的用戶信息。

UserHandler 是一個結(jié)構(gòu)體,它依賴外部存儲接口 store.UserStore,這個接口定義如下:

store/store.go

package store
import "gorm.io/gorm"
type UserStore interface {
	Create(user *User) error
	Get(id int) (*User, error)
}
func NewUserStore(db *gorm.DB) UserStore {
	return &userStore{db}
}
type userStore struct {
	db *gorm.DB
}
func (s *userStore) Create(user *User) error {
	return s.db.Create(user).Error
}
func (s *userStore) Get(id int) (*User, error) {
	var user User
	err := s.db.First(&user, id).Error
	return &user, err
}

store.UserStore 定義了兩個方法,分別用來創(chuàng)建、獲取用戶信息。

User 模型定義如下:

store/model.go

type User struct {
	ID   int    `gorm:"id"`
	Name string `gorm:"name"`
}

store.userStore 結(jié)構(gòu)體則實現(xiàn)了 store.UserStore 接口。

store.userStore 結(jié)構(gòu)體又依賴了 GORM 庫的 *gorm.DB 類型,表示一個數(shù)據(jù)庫連接對象。

我們可以使用 NewMySQLDB 建立數(shù)據(jù)庫連接得到 *gorm.DB 對象:

store/mysql.go

func NewMySQLDB(host, port, user, pass, dbname string) (*gorm.DB, error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		user, pass, host, port, dbname)
	return gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

至此,這個 HTTP 服務(wù)程序整體邏輯就基本介紹完了。

其目錄結(jié)構(gòu)如下:

$ tree
.
├── go.mod
├── go.sum
├── main.go
└── store
    ├── model.go
    ├── mysql.go
    └── store.go

為了保證業(yè)務(wù)的正確性,我們應(yīng)該對 (*UserHandler).CreateUser(*UserHandler).GetUser 這兩個 Handler 進行單元測試。

這兩個 Handler 定義如下:

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.Header().Set("Content-Type", "application/json")
	body, err := io.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		_, _ = fmt.Fprintf(w, `{"msg":"%s"}`, err.Error())
		return
	}
	defer func() { _ = r.Body.Close() }()
	u := store.User{}
	if err := json.Unmarshal(body, &u); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = fmt.Fprintf(w, `{"msg":"%s"}`, err.Error())
		return
	}
	if err := h.store.Create(&u); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = fmt.Fprintf(w, `{"msg":"%s"}`, err.Error())
		return
	}
	w.WriteHeader(http.StatusCreated)
}
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	id := ps[0].Value
	uid, _ := strconv.Atoi(id)
	w.Header().Set("Content-Type", "application/json")
	u, err := h.store.Get(uid)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = fmt.Fprintf(w, `{"msg":"%s"}`, err.Error())
		return
	}
	_, _ = fmt.Fprintf(w, `{"id":%d,"name":"%s"}`, u.ID, u.Name)
}

不過,由于文章篇幅所限,我這里僅以測試 (*UserHandler).GetUser 方法為例,演示如何在測試過程中解決 MySQL 依賴問題,對 (*UserHandler).CreateUser 方法的測試就當(dāng)做作業(yè)留給你自己來完成了(當(dāng)然,你也可以到我的 GitHub 上查看我的實現(xiàn))。

Fake 測試

我們要為 (*UserHandler).GetUser 方法編寫單元測試,首先就要分析下這個方法的外部依賴。

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	id := ps[0].Value
	uid, _ := strconv.Atoi(id)
	w.Header().Set("Content-Type", "application/json")
	u, err := h.store.Get(uid)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = fmt.Fprintf(w, `{"msg":"%s"}`, err.Error())
		return
	}
	_, _ = fmt.Fprintf(w, `{"id":%d,"name":"%s"}`, u.ID, u.Name)
}

UserHandler 結(jié)構(gòu)本身依賴了 store.UserStore,這是一個接口,定義了創(chuàng)建和獲取用戶信息的兩個方法。

我們使用實現(xiàn)了 store.UserStore 接口的 store.userStore 結(jié)構(gòu)體來初始化 UserHandler

func NewUserHandler(db *gorm.DB) *UserHandler {
	return &UserHandler{
		store: store.NewUserStore(db),
	}
}
func NewUserStore(db *gorm.DB) UserStore {
	return &userStore{db}
}

store.userStore 結(jié)構(gòu)體會使用 GORM 來完成對 MySQL 數(shù)據(jù)庫的操作。所以,我們分析出 GetUser 方法的第一個外部依賴實際上就是 MySQL 存儲。

GetUser 方法還接收三個參數(shù),它們都屬于 HTTP 網(wǎng)絡(luò)相關(guān)的外部依賴,你可以在我的另一篇文章《在 Go 語言單元測試中如何解決 HTTP 網(wǎng)絡(luò)依賴問題》中找到解決方案,就不在本文中進行講解了。

所以,我們現(xiàn)在重點要關(guān)注的就只有一個問題,如何解決 MySQL 存儲依賴。

我們來整理下 MySQL 外部依賴的程序調(diào)用鏈:

可以發(fā)現(xiàn),store.UserStore 接口是 UserHandlerstore.userStore 結(jié)構(gòu)體建立連接的橋梁,我們可以將它作為突破口,實現(xiàn)一個 Fake object,來替換 store.userStore 結(jié)構(gòu)體。

所謂 Fake object,其實就是我們同樣要定義一個結(jié)構(gòu)體,并實現(xiàn) CreateGet 兩個方法,以此來實現(xiàn) store.UserStore 接口。

type fakeUserStore struct{}
func (f *fakeUserStore) Create(user *store.User) error {
	return nil
}
func (f *fakeUserStore) Get(id int) (*store.User, error) {
	return &store.User{ID: id, Name: "test"}, nil
}

store.userStore 結(jié)構(gòu)體不同,fakeUserStore 并不依賴 *gorm.DB,也就不涉及 MySQL 數(shù)據(jù)庫操作了,這樣就解決了 MySQL 外部存儲依賴。

(*fakeUserStore).Create 方法沒做任何操作,直接返回 nil,(*fakeUserStore).Get 方法則根據(jù)傳進來的 id 返回固定的 User 信息。這也是 Fake object 的特點,為真實對象實現(xiàn)一個簡化版本。

這樣,我們在編寫測試代碼時,只需要取代 store.userStore 結(jié)構(gòu)體,使用 fakeUserStore 來實例化 UserHandler,就可以避免與 MySQL 數(shù)據(jù)庫打交道了。

handler := &UserHandler{store: &fakeUserStore{}}

(*UserHandler).GetUser 方法編寫的單元測試完整代碼如下:

func TestUserHandler_GetUser_by_fake(t *testing.T) {
	handler := &UserHandler{store: &fakeUserStore{}}
	router := setupRouter(handler)
	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/users/1", nil)
	router.ServeHTTP(w, req)
	assert.Equal(t, 200, w.Code)
	assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
	assert.Equal(t, `{"id":1,"name":"test"}`, w.Body.String())
}

現(xiàn)在被測試的 (*UserHandler).GetUser 方法中通過 h.store.Get(uid) 從數(shù)據(jù)庫中獲取用戶信息時,就不用再去查詢 MySQL 了,而是由 (*fakeUserStore).Get 方法直接返回 Fake 數(shù)據(jù)。

使用 go test 來執(zhí)行測試函數(shù):

$ go test -v -run="TestUserHandler_GetUser_by_fake"
=== RUN   TestUserHandler_GetUser_by_fake
--- PASS: TestUserHandler_GetUser_by_fake (0.00s)
PASS
ok      github.com/jianghushinian/blog-go-example/test/mysql    0.465s

測試通過。

可以發(fā)現(xiàn),使用 Fake 測試來解決 MySQL 外部依賴還是比較簡單的,我們僅需要參考 store.userStore 實現(xiàn)一個簡化版本的 fakeUserStore,然后在測試過程中,使用簡化版本的 fakeUserStore 對象替換掉 store.userStore 即可。

Mock 測試

前文中,我們使用 fakeUserStore 來替換 store.userStore,以此來接口 MySQL 依賴問題。

不過,這種使用 Fake object 來解決外部依賴的方式存在兩個較為常見的弊端:

一個是使用 Fake object 需要手動編寫大量代碼,這里的 store.UserStore 接口僅定義了兩個方法還好,但一個線上的復(fù)雜業(yè)務(wù),可能有幾十個接口,每個接口又有幾十個方法,此時如果還是手動來編寫這些代碼,需要消耗大量時間。

另一個是 Fake object 返回結(jié)果比較固定,如果想測試其他情況,比如查詢的 User 不存在,需要報錯的情況,就得在 (*fakeUserStore).Get 方法中編寫更多的邏輯,這增加了實現(xiàn) Fake object 的復(fù)雜度。

那么有沒有一種替代方案,來彌補 Fake object 的這兩個弊端呢?

答案是使用 Mock 測試。

Mock 和 Fake 類似,本質(zhì)上都是使用一個對象,去替代另一個對象。Fake 測試是實現(xiàn)了一個真實對象(store.userStore)的簡化版本(fakeUserStore),Mock 測試則是使用模擬對象來斷言真實對象被調(diào)用時的輸入符合預(yù)期,然后通過模擬對象返回指定輸出。

在 Go 中,我們可以使用 gomock 來實現(xiàn) Mock 測試。

gomock 項目起源于 Google 的 golang/mock 倉庫。不幸的是,谷歌不再維護這個項目了。幸運的是,這個項目由 Uber fork 了一份,并繼續(xù)維護。

gomock 包含兩個部分:gomock 包和 mockgen 命令行工具。gomock 包用來完成對被 Mock 對象的生命周期管理,mockgen 工具則用來自動生成 Mock 代碼。

可以通過如下方式來安裝 gomock 包和 mockgen 工具:

$ go get go.uber.org/mock/gomock@latest
$ go install go.uber.org/mock/mockgen@latest

注意:在項目根目錄下通過 go get 命令獲取 gomock 包后,不要急著執(zhí)行 go mod tidy,因為現(xiàn)在 gomock 包屬于 indirect 依賴,還沒有被使用。當(dāng)通過 mockgen 工具生成了 Mock 代碼以后,再來執(zhí)行 go mod tidy,go.mod 文件中才不會丟失 gomock 依賴。

要想使用 gomock 來模擬 store.UserStore 接口的實現(xiàn),我們先要使用 mockgen 工具來生成 Mock 代碼:

 $ mockgen -source store/store.go -destination store/mocks/gomock.go -package mocks

-source 參數(shù)指明需要 Mock 的接口文件路徑,即 store.UserStore 接口所在文件。

-destination 參數(shù)指明生成的 Mock 文件路徑。

-package 參數(shù)指明生成的 Mock 文件包名。

在項目根目錄下執(zhí)行 mockgen 命令,即可生成 Mock 文件:

// Code generated by MockGen. DO NOT EDIT.
// Source: store/store.go
// Package mocks is a generated GoMock package.
package mocks
import (
	reflect "reflect"
	store "github.com/jianghushinian/blog-go-example/test/mysql/store"
	gomock "go.uber.org/mock/gomock"
)
// MockUserStore is a mock of UserStore interface.
type MockUserStore struct {
	ctrl     *gomock.Controller
	recorder *MockUserStoreMockRecorder
}
// MockUserStoreMockRecorder is the mock recorder for MockUserStore.
type MockUserStoreMockRecorder struct {
	mock *MockUserStore
}
// NewMockUserStore creates a new mock instance.
func NewMockUserStore(ctrl *gomock.Controller) *MockUserStore {
	mock := &MockUserStore{ctrl: ctrl}
	mock.recorder = &MockUserStoreMockRecorder{mock}
	return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserStore) EXPECT() *MockUserStoreMockRecorder {
	return m.recorder
}
// Create mocks base method.
func (m *MockUserStore) Create(user *store.User) error {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Create", user)
	ret0, _ := ret[0].(error)
	return ret0
}
// Create indicates an expected call of Create.
func (mr *MockUserStoreMockRecorder) Create(user interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUserStore)(nil).Create), user)
}
// Get mocks base method.
func (m *MockUserStore) Get(id int) (*store.User, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Get", id)
	ret0, _ := ret[0].(*store.User)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}
// Get indicates an expected call of Get.
func (mr *MockUserStoreMockRecorder) Get(id interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockUserStore)(nil).Get), id)
}

提示:生成的 mocks 包代碼你無需全部看懂,僅知道它大概生成了什么內(nèi)容,如何使用即可。

可以發(fā)現(xiàn),mockgen 為我們生成了 mocks.MockUserStore 結(jié)構(gòu)體,并且實現(xiàn)了 Create、Get 兩個方法,即實現(xiàn)了 store.UserStore 接口。

現(xiàn)在,我們就可以使用生成的 Mock 對象來編寫單元測試代碼了:

func TestUserHandler_GetUser_by_mock(t *testing.T) {
	ctrl := gomock.NewController(t)
	// 斷言 mockUserStore.Get 方法會被調(diào)用
	defer ctrl.Finish()
	mockUserStore := mocks.NewMockUserStore(ctrl)
	mockUserStore.EXPECT().Get(2).Return(&store.User{
		ID:   2,
		Name: "user2",
	}, nil)
	handler := &UserHandler{store: mockUserStore}
	router := setupRouter(handler)
	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/users/2", nil)
	router.ServeHTTP(w, req)
	assert.Equal(t, 200, w.Code)
	assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
	assert.Equal(t, `{"id":2,"name":"user2"}`, w.Body.String())
}

gomock.NewController(t) 用來創(chuàng)建一個 Mock 控制器,該對象可以控制整個 Mock 生命周期。

ctrl.Finish() 用來斷言 Mock 對象使用 EXPECT() 方法設(shè)置的期待執(zhí)行方法會被調(diào)用,一般使用 defer 語句來調(diào)用,防止最后忘記。不過,如果你使用的 Go 版本大于 1.14,則可以不必顯式調(diào)用 ctrl.Finish()。

mocks.NewMockUserStore(ctrl) 使用 Mock 控制器創(chuàng)建了 *mocks.MockUserStore 對象,有了它,我們就可以模擬調(diào)用 store.UserStore 接口對應(yīng)方法的邏輯了:

mockUserStore.EXPECT().Get(2).Return(&store.User{
    ID:   2,
    Name: "user2",
}, nil)

mockUserStore 對象就相當(dāng)于我們前文中實現(xiàn)的 fakeUserStore。

Mock 對象的 EXPECT() 方法用來設(shè)置預(yù)期被調(diào)用的方法,以及被調(diào)用方法所期望的輸入,它支持鏈?zhǔn)秸{(diào)用,.Get(2) 表示期望在測試中調(diào)用 Mock 對象 mockUserStoreGet 方法時,輸入?yún)?shù)是 2,Return 方法用來設(shè)置輸出,即返回值內(nèi)容。

這就相當(dāng)于,我們實現(xiàn)了 fakeUserStoreGet 方法。

我們可以使用 mockUserStore 來實例化 UserHandler 對象。

req 請求中,我們設(shè)置請求的用戶 ID 值為 2,即 mockUserStore 對象斷言中的參數(shù),二者參數(shù)匹配,Mock 對象才能生效。

單元測試最后,斷言了返回結(jié)果為 {"id":2,"name":"user2"},即 mockUserStore 對象期望的返回結(jié)果。

現(xiàn)在我們就可以測試 (*UserHandler).GetUser 方法了。

使用 go test 來執(zhí)行測試函數(shù):

$ go test -v -run="TestUserHandler_GetUser_by_mock"
=== RUN   TestUserHandler_GetUser_by_mock
--- PASS: TestUserHandler_GetUser_by_mock (0.00s)
PASS
ok      github.com/jianghushinian/blog-go-example/test/mysql    0.220s

測試通過。

使用 Mock 測試來解決 MySQL 外部依賴問題,我們無需手動編寫 Mock 對象的代碼,可以使用 mockgen 工具為我們自動生成,簡化了 Fake 測試中編寫 fakeUserStore 的過程。

并且,如果想要測試其他情況,僅需要再次使用 Mock 對象的 EXPECT() 方法來設(shè)置 Get 方法的期望輸入和輸出即可。

比如設(shè)置預(yù)期查詢 ID 為 3 的用戶信息時,返回 user not found 錯誤:

mockUserStore.EXPECT().Get(3).Return(nil, errors.New("user not found"))

Mock 測試更方便我們測試不同業(yè)務(wù)場景。

gomock 更多用法

gomock 還有一些使用技巧值得分享。

mockgen

前文中,我們使用 mockgen 通過指定源碼文件形式生成了 Mock 代碼:

$ mockgen -source store/store.go -destination store/mocks/gomock.go -package mocks

mockgen 工具還支持通過反射模式來生成 Mock 代碼:

$ mockgen -package mocks -destination store/mocks/gomock.go github.com/jianghushinian/blog-go-example/test/mysql/store UserStore

命令最后的兩個參數(shù)分別代表需要生成 Mock 代碼的包的導(dǎo)入路徑和逗號分隔的接口列表。

執(zhí)行以上命令同樣能夠成功生成 Mock 代碼。

此外,我們還可以將 mockgen 命令寫到 Go 文件中,然后使用 Go generate 工具來生成 Mock 代碼:

store/generate.go

package store
//go:generate mockgen -package mocks -destination ./mocks/gomock.go . UserStore

這次我們的 mockgen 命令又有所不同,包的導(dǎo)入路徑僅為一個 .,表示當(dāng)前目錄,這也是被支持的。

這時候,我們只需要在項目根目錄下執(zhí)行 go generate ./... 命令即可生成 Mock 代碼。./... 表示查找項目下全部文件,go generate 會自動找到帶有 //go:generate 注釋的命令并執(zhí)行。

如果我們有多個源碼文件要生成 Mock 代碼,go generate 方式就非常合適,僅需要在 Go 文件中分多行依次寫出 mockgen 命令即可使用一條命令一次全部生成。

gomock

前文中,我們使用了 Mock 對象 mockUserStoreEXPECT() 方法來設(shè)置 Get 方法所期待的輸入和輸出。

mockUserStore.EXPECT().Get(2).Return(&store.User{
    ID:   2,
    Name: "user2",
}, nil)

有時候,EXPECT() 所作用的方法可能存在多個參數(shù),且有些參數(shù)不容易模擬,比如最常見的 context.Context 參數(shù),針對這些情況,gomock 提供了更多的參數(shù)匹配方法:

gomock.Any() 表示匹配任意參數(shù),適合參數(shù)模擬困難的情況。

gomock.Eq(x) 表示匹配與 x 相等的參數(shù)。

gomock.Not(x) 表示匹配與 x 不想等的參數(shù)。

gomock.Nil() 表示匹配 nil 參數(shù)。

gomock.Len(i) 表示匹配長度為 i 的參數(shù)。

gomock.All(ms) 表示傳入的所有參數(shù)都想等才能匹配。

以上這些參數(shù)匹配方法都可以像如下這樣使用:

mockUserStore.EXPECT().Get(gomock.Eq(2)).Return(&store.User{
    ID:   2,
    Name: "user2",
}, nil)

此外,我們可以約束 EXPECT() 所作用方法的執(zhí)行次數(shù):

.Return(xxx).Times(2) // 預(yù)期方法會被調(diào)用 2 次
.Return(xxx).MaxTimes(2) // 預(yù)期方法最多執(zhí)行 2 次
.Return(xxx).MinTimes(2) // 預(yù)期方法至少執(zhí)行 2 次
.Return(xxx).AnyTimes() // 預(yù)期方法執(zhí)行任意次都能匹配

還可以約束 EXPECT() 所作用方法的執(zhí)行順序:

.Return(xxx).After(preReq) // 當(dāng)前預(yù)期方法在 preReq 預(yù)期方法執(zhí)行完成之后執(zhí)行

以上便是我認為 gomock 中比較常用的功能講解,更多功能可參考官方文檔

總結(jié)

本文向大家介紹了在 Go 中編寫單元測試時,如何解決 MySQL 外部依賴的問題。

我們分別使用了 Fake object 和 Mock 兩種方式,來替換原有的外部依賴。

Web 服務(wù)的代碼不是隨意設(shè)計的,有意將 UserHandler 依賴的類型設(shè)為 store.UserStore 接口,而不是 store.userStore 結(jié)構(gòu)體,是為了解耦。通過使用接口,解決了 UserHandlerstore.userStore 結(jié)構(gòu)體強綁定的問題,這就給我們使用 fakeUserStoremockUserStore 來替代 store.userStore 創(chuàng)造了機會。

可以發(fā)現(xiàn),本文介紹的兩種方法其實不僅能夠用于解決 MySQL 外部依賴問題。任何使用接口編寫的代碼,在測試時都可以使用這兩種方式來替換依賴。這就是 Go 面向接口編程的好處。

以上就是詳解Go語言單元測試中如何解決MySQL存儲依賴問題的詳細內(nèi)容,更多關(guān)于Go單元測試解決MySQL存儲依賴的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang Http 驗證碼示例實現(xiàn)

    Golang Http 驗證碼示例實現(xiàn)

    這篇文章主要介紹了Golang Http 驗證碼示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 一文詳解Go語言單元測試的原理與使用

    一文詳解Go語言單元測試的原理與使用

    Go語言中自帶有一個輕量級的測試框架testing和自帶的go?test命令來實現(xiàn)單元測試和性能測試。本文將通過示例詳細聊聊Go語言單元測試的原理與使用,需要的可以參考一下
    2022-09-09
  • Ubuntu安裝Go語言運行環(huán)境

    Ubuntu安裝Go語言運行環(huán)境

    由于最近偏愛Ubuntu,在加上作為一門開源語言,在Linux上從源代碼開始搭建環(huán)境更讓人覺得有趣味性。讓我們直接先從Go語言的環(huán)境搭建開始
    2015-04-04
  • Go語言如何通過通信共享內(nèi)存

    Go語言如何通過通信共享內(nèi)存

    這篇文章主要為大家介紹了Go語言如何通過通信共享內(nèi)存實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • gtoken替換jwt實現(xiàn)sso登錄的排雷避坑

    gtoken替換jwt實現(xiàn)sso登錄的排雷避坑

    這篇文章主要為大家介紹了gtoken替換jwt實現(xiàn)sso登錄的排雷避坑,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • go語言實現(xiàn)sqrt的方法

    go語言實現(xiàn)sqrt的方法

    這篇文章主要介紹了go語言實現(xiàn)sqrt的方法,實例分析了Go語言實現(xiàn)計算平方根的技巧,需要的朋友可以參考下
    2015-03-03
  • mayfly-go部署和使用詳解

    mayfly-go部署和使用詳解

    這篇文章主要介紹了mayfly-go部署和使用詳解,此處部署基于CentOS7.4部署,結(jié)合實例代碼圖文給大家講解的非常詳細,需要的朋友可以參考下
    2022-09-09
  • GOLANG使用Context實現(xiàn)傳值、超時和取消的方法

    GOLANG使用Context實現(xiàn)傳值、超時和取消的方法

    這篇文章主要介紹了GOLANG使用Context實現(xiàn)傳值、超時和取消的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • 一文搞懂Go?Exec?僵尸與孤兒進程

    一文搞懂Go?Exec?僵尸與孤兒進程

    本文主要介紹了Go?Exec?僵尸與孤兒進程,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Go語言自定義linter靜態(tài)檢查工具

    Go語言自定義linter靜態(tài)檢查工具

    這篇文章主要介紹了Go語言自定義linter靜態(tài)檢查工具,Go語言是一門編譯型語言,編譯器將高級語言翻譯成機器語言,會先對源代碼做詞法分析,詞法分析是將字符序列轉(zhuǎn)換為Token序列的過程,文章詳細介紹需要的小伙伴可以參考一下
    2022-05-05

最新評論