Golang HTTP Handler测试中如何将内部真实DB依赖替换为Mock实现
核心问题
UserHandler结构体的userDb字段使用了*UserDB具体结构体类型,而非你预先定义的UserDBFunctions接口类型,Go的强类型约束下无法注入其他实现了该接口的Mock实例。- 测试侧重复定义
FakeUserDBFunctions接口属于冗余代码,直接复用已有的UserDBFunctions即可。 *UserDB(&FakeUserDB{})类型转换不符合Go语法规则,不同结构体即便实现了同一接口也无法直接强转。
调整后的可运行代码
package main import ( "log" "testing" ) // DB functions type UserDBFunctions interface { InsertUser() string } type UserDB struct { } func (u UserDB) InsertUser() string { return "hello" } // http handler functions type UserHandlerFunctions interface { Register() string } type UserHandler struct { // 核心修改:将字段类型改为接口,支持任意实现该接口的实例注入 userDb UserDBFunctions } func newUserHandler() UserHandler { return UserHandler{userDb: new(UserDB)} } func (u UserHandler) Register() string { str := u.userDb.InsertUser() return str + " world" } func main() { var i UserHandlerFunctions = newUserHandler() str := i.Register() log.Println(str) // 输出:hello world } // test functions type FakeUserDB struct { } // FakeUserDB 实现了UserDBFunctions接口,可直接注入 func (u FakeUserDB) InsertUser() string { return "bye" } func newFakeUserHandler() UserHandler { // 直接注入Mock实例即可,无需强转 return UserHandler{userDb: FakeUserDB{}} } func TestRegisterUser(t *testing.T) { t.Run("register user", func(t *testing.T) { var i UserHandlerFunctions = newFakeUserHandler() str := i.Register() log.Println(str) // 输出:bye world // 可加断言验证结果符合预期 if str != "bye world" { t.Errorf("expect bye world, got %s", str) } }) }
验证说明
- 原有业务逻辑完全没有改动,
Register方法的实现和之前完全一致 - 生产环境调用
newUserHandler使用真实DB实现,测试环境直接注入Mock DB实现,两者完全解耦 - 后续如果需要更换DB实现,只要实现
UserDBFunctions接口即可无缝替换,不需要修改Handler层代码
内容的提问来源于stack exchange,提问作者Erik Bent
相关产品推荐
相关产品推荐

