假设我们有一个DBClient
结构体,它包含一个*sql.DB
字段,用于进行数据库操作:
type DBClient struct {
Conn *sql.DB
}
我们可以为这个结构体定义一个接口:
type DBClientInterface interface {
GetUser(id int) (User, error)
Login(username, password string) (bool, error)
}
然后,我们可以在DBClient
结构体中实现这个接口:
func (db *DBClient) GetUser(id int) (User, error) {
// 实现获取用户的逻辑...
}
func (db *DBClient) Login(username, password string) (bool, error) {
// 实现登录的逻辑...
}
有了这个接口,我们就可以在测试中使用GoMock来创建DBClientInterface
的模拟对象。
与之前的示例类似,我们可以使用gomock.Controller
来创建模拟对象,并设置它的行为:
func TestDBClient_GetUser(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDBClient := NewMockDBClientInterface(ctrl)
u := User{ID: 1, Name: "Alice"}
mockDBClient.EXPECT().GetUser(1).Return(u, nil)
user, err := mockDBClient.GetUser(1)
if err != nil {
t.Fatalf("expect no error, but got %v", err)
}
if user.Name != "Alice" {
t.Fatalf("expect name Alice, but got %s", user.Name)
}
}
func TestDBClient_Login(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDBClient := NewMockDBClientInterface(ctrl)
mockDBClient.EXPECT().Login("Alice", "password").Return(true, nil)
success, err := mockDBClient.Login("Alice", "password")
if err != nil {
t.Fatalf("expect no error, but got %v", err)
}
if !success {
t.Fatalf("expect success, but got failure")
}
}
在这些测试中,我们都是通过创建模拟对象和设置模拟对象的行为来进行测试的。注意,即使我们的代码是直接实现的,只要我们能为这些实现定义一个接口,我们就可以使用GoMock工具进行测试。
总的来说,通过引入接口和使用GoMock工具,我们可以很方便地进行数据库操作的单元测试,不论我们的代码是如何实现的。这样,我们就可以更加自信地进行开发,因为我们知道任何新的改动都不会无意中破坏现有的功能。