如何在golang中测试映射对象



我尝试在我的go代码中实现一些单元测试,并发现模拟方法的主题相当困难。我有下面的例子,希望你能帮助我:)

在第一层我有以下代码:

package api
import (
"fmt"
"core"
)
type createUserDTO struct {
Id string
}
func ApiMethod() {
fmt.Println("some incoming api call wit user")
incomingUserData := &createUserDTO{Id: "testId"}
mapedUser := incomingUserData.mapUser()
mapedUser.Create()
}
func (createUserDTO *createUserDTO) mapUser() core.User {
return &core.UserCore{Id: createUserDTO.Id}
}

第二层代码如下:

package core
import (
"fmt"
)
type CoreUser struct{ Id string }
type User interface {
Create()
}
func (user CoreUser) Create() {
fmt.Println("Do Stuff")
}

我现在的问题是,我如何测试api包中的每个公共方法而不测试核心包。特别是Create()方法

根据这些评论,我把一个简单的GitHub存储库放在一起,以展示我通常如何处理Go中的结构化项目。存储库目前还没有考虑到测试部分,但是用这个项目结构插入测试部分应该很容易。
让我们从通用文件夹布局开始:

  • controllers
  • services
  • db
  • dto
  • models

现在,让我们看看相关的文件。

models/user.go
package models
import "gorm.io/gorm"
type User struct {
*gorm.Model
Id string `gorm:"primaryKey"`
}
func NewUser(id string) *User {
return &User{Id: id}
}

简单的结构定义。

dto/user.go
package dto
import "time"
type UserDTO struct {
Id      string    `json:"id"`
AddedOn time.Time `json:"added_on"`
}
func NewUserDTO(id string) *UserDTO {
return &UserDTO{Id: id}
}

我们用一个虚拟的AddedOn字段来丰富模型结构体,这只是为了演示的需要。

db/user.go
package db
import (
"gorm.io/gorm"
"userapp/models"
)
type UserDb struct {
Conn *gorm.DB
}
type UserDbInterface interface {
SaveUser(user *models.User) error
}
func (u *UserDb) SaveUser(user *models.User) error {
if dbTrn := u.Conn.Create(user); dbTrn.Error != nil {
return dbTrn.Error
}
return nil
}

在这里,我们定义了一个使用User存储库的接口。在我们的测试中,我们可以提供UserDb结构体的模拟而不是实例。

services/user.go
package services
import (
"time"
"userapp/db"
"userapp/dto"
"userapp/models"
)
type UserService struct {
DB db.UserDbInterface
}
type UserServiceInterface interface {
AddUser(inputReq *dto.UserDTO) (*dto.UserDTO, error)
}
func NewUserService(db db.UserDbInterface) *UserService {
return &UserService{
DB: db,
}
}
func (u *UserService) AddUser(inputReq *dto.UserDTO) (*dto.UserDTO, error) {
// here you can write complex logic
user := models.NewUser(inputReq.Id)
// invoke db repo
if err := u.DB.SaveUser(user); err != nil {
return nil, err
}
inputReq.AddedOn = time.Now()
return inputReq, nil
}

这是在表示层和底层存储库之间架起连接的层。

controllers/user.go
package controllers
import (
"encoding/json"
"io"
"net/http"
"userapp/dto"
"userapp/services"
)
type UserController struct {
US services.UserServiceInterface
}
func NewUserController(userService services.UserServiceInterface) *UserController {
return &UserController{
US: userService,
}
}
func (u *UserController) Save(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
panic(err)
}
r.Body.Close()
var userReq dto.UserDTO
json.Unmarshal(reqBody, &userReq)
userRes, err := u.US.AddUser(&userReq)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(err)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(userRes)
}

这里,我们定义了(通过依赖注入)使用UserService结构体的控制器。

你可以在GitHub上找到我的仓库如果能让你明白一点,请告诉我。