当前位置: 首页 > 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)
}
http://www.xdnf.cn/news/706177.html

相关文章:

  • [Go] Option选项设计模式 — — 编程方式基础入门
  • 驱动开发(2)|鲁班猫rk3568简单GPIO波形操控
  • 2025年数字经济与绿色金融国际会议:智能金融与可持续发展的创新之路
  • Vue Hook Store 设计模式最佳实践指南
  • 计算机操作系统(十四)互斥锁,信号量机制与整型信号量
  • C语言文件读取中文乱码问题解析与解决方案
  • Spring boot集成milvus(spring ai)
  • 员工管理系统 (Python实现)
  • 智能手机上用Termux安装php+Nginx
  • 金融欺诈有哪些检测手段
  • 关于AWESOME-DIGITAL-HUMAN的部署
  • 【HW系列】—C2远控服务器(webshell链接工具, metasploit、cobaltstrike)的漏洞特征流量特征
  • 38. 自动化测试异步开发之编写客户端异步webdriver接口类
  • 基于ELK的分布式日志实时分析与可视化系统设计
  • 每日刷题c++
  • UE5蓝图中播放背景音乐和使用代码播放声音
  • 100个 Coze 智能体实战案例
  • tiktoken学习
  • C54-动态开辟内存空间
  • Java交互协议详解:深入探索通信机制
  • 【Linux笔记】Shell-脚本(下)|(常用命令详细版)
  • 基于随机函数链接神经网络(RVFL)的锂电池健康状态(SOH)预测
  • ICASSP2025丨融合语音停顿信息与语言模型的阿尔兹海默病检测
  • .NET 开源工业视觉系统 OpenIVS 快速搭建自动化检测平台
  • 智能仓储落地:机器人如何通过自动化减少仓库操作失误?
  • 自动化中的伦理:驯服人工智能中的偏见与守护合规之路
  • Magentic-UI:人机协作的网页自动化革命
  • Mybatis中实现多表查询(多对一)
  • 【Hive 运维实战】一键管理 Hive 服务:Metastore 与 HiveServer2 控制脚本开发与实践
  • 上传图片转成3D VR效果 / 用photo-sphere-viewer实现图片VR效果