在 golang 框架中編寫可測試的代碼至關(guān)重要,可通過以下最佳實踐實現(xiàn):使用接口定義行為約定,以便輕松模擬依賴項。采用依賴注入模式,允許注入依賴項以方便測試。利用模擬技術(shù)創(chuàng)建假依賴項實例,以控制其行為并測試特定交互。
如何在 Golang 框架中編寫可測試的代碼
在 Go 框架中編寫可測試的代碼對于確保代碼的可靠性和可維護(hù)性至關(guān)重要。遵循一些簡單的最佳實踐,可以輕松地編寫可測試的代碼。
使用接口
接口是編寫可測試代碼的關(guān)鍵部分。它們允許您定義行為約定,而無需具體實現(xiàn)。通過使用接口,您可以輕松地模擬依賴項,而無需修改實際代碼。
// 這是一個定義了一個 `GetUser` 方法的 `UserService` 接口。 type UserService interface { GetUser(id int64) (*User, error) }
登錄后復(fù)制
依賴注入
依賴注入是一種設(shè)計模式,允許您將依賴項注入函數(shù)或結(jié)構(gòu)。這使您可以輕松地交換依賴項,以進(jìn)行測試和其他目的。
// 這個函數(shù)使用 `UserService` 接口獲取 `User`。 func GetUser(userId int64, userService UserService) (*User, error) { return userService.GetUser(userId) }
登錄后復(fù)制
模擬
模擬是一種創(chuàng)建依賴項假實例的技術(shù),能夠控制其行為。這對于測試函數(shù)或結(jié)構(gòu)與依賴項交互的方式非常有用。
import "testing" func TestGetUser(t *testing.T) { // 創(chuàng)建一個模擬的 `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ù)制
實戰(zhàn)案例
考慮一個簡單的 Gin 路由,它接受一個 Post 請求,并使用 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ù)測試這個路由:
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)建一個 HTTP 請求。 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 請求。 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ù)制