当前位置: 首页 > news >正文

快速了解 GO 之依赖注入与 mock测试

更多个人笔记见:
github个人笔记仓库
gitee 个人笔记仓库
个人学习,学习过程中还会不断补充~ (后续会更新在github上)

文章目录

    • 例子分析依赖注入
        • 传统方式
        • DI方式
            • 构造函数注入依赖
            • 容器式注入
    • (补充)Mock测试

例子分析依赖注入

  • 依赖注入(DI)是一种设计模式,通过外部传入依赖对象而非内部创建
  • 解耦是目标,DI是实现手段
传统方式
//传统方式(紧耦合)
func GetUser(id uint) {db := gorm.Open(...) // 内部创建依赖db.First(...)
}func GetUser(db *gorm.DB, id uint) { // 依赖从外部注入db.First(...)
}
DI方式

真正的依赖注入应该: 依赖抽象而非具体实现,通过接口解耦并便于单元测试

// DI方式(松耦合)
// 1. 定义接口(抽象层)
type Repository interface{...}// 2. 实现具体存储库
type GormRepository struct{...}// 3. 业务服务声明依赖接口
type UserService struct {repo Repository
}// 4. 依赖注入点(通常在main/初始化代码)
func main() {gormDB := initGorm() // 初始化具体DB连接,这个地方是初始化,自己定义的// 将具体实现注入抽象接口!!!!!!service := UserService{repo: &GormRepository{db: gormDB}}//接下来 UserService的方法就可以实现直接调用->进一步调用GormRepository(也就是满足Repository接口的结构体的具体方法)
}

具体例子分析:

// 定义数据模型
type User struct {ID       uintName     stringEmail    stringPassword string
}// 1. 定义接口(抽象层)
type Repository interface {FindUserByID(id uint) (*User, error)CreateUser(user *User) errorUpdateUser(user *User) errorDeleteUser(id uint) error
}
// 2. 实现具体存储库
type GormRepository struct {db *gorm.DB
}// 实现Repository接口的方法
func (r *GormRepository) FindUserByID(id uint) (*User, error) {var user Userresult := r.db.First(&user, id)if result.Error != nil {return nil, result.Error}return &user, nil
}func (r *GormRepository) CreateUser(user *User) error {return r.db.Create(user).Error
}func (r *GormRepository) UpdateUser(user *User) error {return r.db.Save(user).Error
}func (r *GormRepository) DeleteUser(id uint) error {return r.db.Delete(&User{}, id).Error
}
// 3. 业务服务声明依赖接口
type UserService struct {repo Repository
}// 业务方法使用注入的Repository  这里不是需要满足Repository,都是实际业务方法
//repo 只是向下进一步调用
func (s *UserService) GetUser(id uint) (*User, error) {return s.repo.FindUserByID(id) //调用对应的FindUserByID方法
}func (s *UserService) RegisterUser(name, email, password string) (*User, error) {user := &User{Name:     name,Email:    email,Password: password, // 实际应用中应该加密}err := s.repo.CreateUser(user)if err != nil {return nil, fmt.Errorf("failed to register user: %w", err)}return user, nil
}func (s *UserService) UpdateUserProfile(id uint, name string) error {user, err := s.repo.FindUserByID(id)if err != nil {return fmt.Errorf("user not found: %w", err)}user.Name = namereturn s.repo.UpdateUser(user)
}// 4. 依赖注入点(通常在main/初始化代码)
func main() {// 初始化数据库连接,这里是可以修改的gormDB := initGorm()// 将具体实现注入抽象接口,而不是内部使用具体的userService := &UserService{repo: &GormRepository{db: gormDB},}// 使用服务user, err := userService.GetUser(1)if err != nil {fmt.Printf("Error getting user: %v\n", err)return}fmt.Printf("Found user: %s (%s)\n", user.Name, user.Email)// 注册新用户newUser, err := userService.RegisterUser("John Doe", "john@example.com", "password123")if err != nil {fmt.Printf("Error registering user: %v\n", err)return}fmt.Printf("Registered new user with ID: %d\n", newUser.ID)
}// 初始化GORM数据库连接,对应 main 一开始
func initGorm() *gorm.DB {db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}// 自动迁移db.AutoMigrate(&User{})return db
}
构造函数注入依赖

在 main 中注入的时候可以修改:

// 使用构造函数注入依赖
func NewUserService(repo Repository) *UserService {return &UserService{repo: repo,}
}
// 使用示例
func main() {gormDB := initGorm()repo := &GormRepository{db: gormDB}// 通过构造函数注入依赖userService := NewUserService(repo)// 使用服务...
}

在每一层都可以使用函数方式的注入,所以代码中能看到很多“New”

容器式注入
// 简单的DI容器
type Container struct {services map[string]interface{}
}func NewContainer() *Container {return &Container{services: make(map[string]interface{}),}
}func (c *Container) Register(name string, service interface{}) {c.services[name] = service
}func (c *Container) Get(name string) interface{} {return c.services[name]
}// 使用容器
func main() {container := NewContainer()// 注册服务gormDB := initGorm()container.Register("repository", &GormRepository{db: gormDB})container.Register("userService", NewUserService(container.Get("repository").(Repository)))// 获取服务userService := container.Get("userService").(*UserService)// 使用服务...
}

(补充)Mock测试

// 用于测试的Mock存储库
type MockRepository struct {users map[uint]*User
}//构造函数但是没有注入依赖,因为只是初始化了一个内部状态(`users` 映射),但没有接收任何外部依赖
func NewMockRepository() *MockRepository {return &MockRepository{users: make(map[uint]*User),  //**​初始时确实没有存储任何用户数据**}
}func (r *MockRepository) FindUserByID(id uint) (*User, error) {user, exists := r.users[id]if !exists {return nil, fmt.Errorf("user not found")}return user, nil
}func (r *MockRepository) CreateUser(user *User) error {if user.ID == 0 {user.ID = uint(len(r.users) + 1)}r.users[user.ID] = userreturn nil
}func (r *MockRepository) UpdateUser(user *User) error {if _, exists := r.users[user.ID]; !exists {return fmt.Errorf("user not found")}r.users[user.ID] = userreturn nil
}func (r *MockRepository) DeleteUser(id uint) error {if _, exists := r.users[id]; !exists {return fmt.Errorf("user not found")}delete(r.users, id)return nil
}// 测试示例,这里用到了 GO 中的测试!
func TestUserService() {// 使用Mock存储库进行测试mockRepo := NewMockRepository()  //利用函数初始化userService := &UserService{repo: mockRepo}  //mockRepo是满足对应的 Repository 接口的方法的//接下来可以使用UserService 的结构体方法了// 测试注册用户user, _ := userService.RegisterUser("Test User", "test@example.com", "password")// 测试获取用户foundUser, _ := userService.GetUser(user.ID) //GetUser中会调用mockRepo的方法的fmt.Printf("Found user in test: %s\n", foundUser.Name)
}

相关文章:

  • 【Elasticsearch】exists` 查询用于判断文档中是否存在某个指定字段。它检查字段是否存在于文档中,并且字段的值不为 `null`
  • Ubuntu Zabbix 钉钉报警
  • 3 分钟学会使用 Puppeteer 将 HTML 转 PDF
  • RK3568DAYU开发板-平台驱动开发--UART
  • STP配置
  • 【ConvLSTM第一期】ConvLSTM原理
  • day13 leetcode-hot100-24(链表3)
  • c++ opencv 形态学操作腐蚀和膨胀
  • OpenCV CUDA模块结构分析与形状描述符------在 GPU 上计算图像的原始矩(spatial moments)函数spatialMoments()
  • RV1126-OPENCV Mat理解
  • 基于React和TypeScript的金融市场模拟器开发与模式分析
  • 从 SWT Browser 迁移到 JxBrowser
  • C#·常用快捷键
  • kibana解析Excel文件,生成mapping es导入Excel
  • 职坐标AI算法实战:TensorFlow/PyTorch深度模型
  • Typora-macOS 风格代码块
  • K8S查看pod资源占用和物理机器IP对应关系
  • 小白的进阶之路系列之七----人工智能从初步到精通pytorch自动微分优化以及载入和保存模型
  • 【C++项目】:仿 muduo 库 One-Thread-One-Loop 式并发服务器
  • 解释滚动更新的过程,如何通过`kubectl set image`命令触发更新? 版本回滚的命令是什么?如何查看Deployment的更新历史?
  • 深圳信息公司做关键词/seo标题优化导师咨询
  • 网站建设怎么申请域名/app营销策略有哪些
  • 肇庆住房建设部网站/全网营销代运营
  • 龙口网站建设/中小企业管理培训班
  • 网站建设金华/怎么申请自己的网络平台
  • 做b2b需要建网站吗/淘宝店铺如何推广