在 golang 框架中編寫可測(cè)試的代碼至關(guān)重要,可通過以下最佳實(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您定義行為約定,而無需具體實(shí)現(xiàn)。通過使用接口,您可以輕松地模擬依賴項(xiàng),而無需修改實(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ù)庫中獲取用戶。
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ù)制






