在 golang 框架中編寫可測(cè)試的代碼至關(guān)重要,可通過(guò)以下最佳實(shí)踐實(shí)現(xiàn):使用接口定義行為約定,以便輕松模擬依賴項(xiàng)。采用依賴注入模式,允許注入依賴項(xiàng)以方便測(cè)試。利用模擬技術(shù)創(chuàng)建假依賴項(xiàng)實(shí)例,以控制其行為并測(cè)試特定交互。
如何在 Golang 框架中編寫可測(cè)試的代碼
在 Go 框架中編寫可測(cè)試的代碼對(duì)于確保代碼的可靠性和可維護(hù)性至關(guān)重要。遵循一些簡(jiǎn)單的最佳實(shí)踐,可以輕松地編寫可測(cè)試的代碼。
使用接口
接口是編寫可測(cè)試代碼的關(guān)鍵部分。它們?cè)试S您定義行為約定,而無(wú)需具體實(shí)現(xiàn)。通過(guò)使用接口,您可以輕松地模擬依賴項(xiàng),而無(wú)需修改實(shí)際代碼。
// 這是一個(gè)定義了一個(gè) `GetUser` 方法的 `UserService` 接口。 type UserService interface { GetUser(id int64) (*User, error) }
登錄后復(fù)制
依賴注入
依賴注入是一種設(shè)計(jì)模式,允許您將依賴項(xiàng)注入函數(shù)或結(jié)構(gòu)。這使您可以輕松地交換依賴項(xiàng),以進(jìn)行測(cè)試和其他目的。
// 這個(gè)函數(shù)使用 `UserService` 接口獲取 `User`。 func GetUser(userId int64, userService UserService) (*User, error) { return userService.GetUser(userId) }
登錄后復(fù)制
模擬
模擬是一種創(chuàng)建依賴項(xiàng)假實(shí)例的技術(shù),能夠控制其行為。這對(duì)于測(cè)試函數(shù)或結(jié)構(gòu)與依賴項(xiàng)交互的方式非常有用。
import "testing" func TestGetUser(t *testing.T) { // 創(chuàng)建一個(gè)模擬的 `UserService`。 userService := &UserServiceMock{} userService.On("GetUser").Return(&User{Name: "John"}, nil) // 使用模擬的 `UserService` 調(diào)用 `GetUser`。 user, err := GetUser(1, userService) if err != nil { t.Errorf("GetUser() returned unexpected error: %v", err) } // 斷言返回的用戶與預(yù)期的一致。 if user.Name != "John" { t.Errorf("GetUser() returned unexpected user: %v", user) } }
登錄后復(fù)制
實(shí)戰(zhàn)案例
考慮一個(gè)簡(jiǎn)單的 Gin 路由,它接受一個(gè) Post 請(qǐng)求,并使用 userService 從數(shù)據(jù)庫(kù)中獲取用戶。
package main import ( "<a style='color:#f60; text-decoration:underline;' href="https://www.php.cn/zt/15841.html" target="_blank">git</a>hub.com/gin-gonic/gin" ) type User struct { ID int64 Name string } type UserService interface { GetUser(id int64) (*User, error) } func main() { userService := &UserServiceImpl{} router := gin.Default() router.POST("/users/:id", getUser(userService)) } func getUser(userService UserService) gin.HandlerFunc { return func(c *gin.Context) { id, _ := strconv.ParseInt(c.Param("id"), 10, 64) user, err := userService.GetUser(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, user) } }
登錄后復(fù)制
我們可以使用上面討論的技術(shù)測(cè)試這個(gè)路由:
import ( "bytes" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" ) func TestGetUser(t *testing.T) { userService := &UserServiceMock{} userService.On("GetUser").Return(&User{ID: 1, Name: "John"}, nil) router := gin.Default() router.POST("/users/:id", getUser(userService)) // 創(chuàng)建一個(gè) HTTP 請(qǐng)求。 url := fmt.Sprintf("http://localhost:8080/users/%d", 1) req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer([]byte(""))) if err != nil { t.Fatal(err) } // 執(zhí)行 HTTP 請(qǐng)求。 rr := httptest.NewRecorder() router.ServeHTTP(rr, req) // 斷言 HTTP 響應(yīng)狀態(tài)。 if status := rr.Code; status != http.StatusOK { t.Errorf("Unexpected HTTP status code: %d", status) } // 解析 HTTP 響應(yīng)正文。 var user User if err := json.NewDecoder(rr.Body).Decode(&user); err != nil { t.Fatal(err) } // 斷言返回的用戶與預(yù)期的一致。 if user.ID != 1 || user.Name != "John" { t.Errorf("Unexpected user: %v", user) } }
登錄后復(fù)制