Go中如何将Protobuf对象传入sqlmock.AddRow?测试遇字段为空
问题解决:SQLMock测试GORM代码时user.name为nil的修复方案
核心问题分析
- 结构体字段不可导出:Go语言中首字母小写的字段(如
user.name)属于包私有成员,GORM无法通过反射机制为其赋值,导致从数据库查询后该字段始终为nil。 - SQLMock列与GORM映射不匹配:你定义的SQLMock行列名(
user_id、user_name)未覆盖GORM查询所需的关键字段(如uuid),且列名与结构体字段的映射关系不对。 - 误解Protobuf传入方式:
AddRow不需要直接传入Protobuf对象,只需传入数据库原始数据,Protobuf转换由结构体的Protobuf()方法处理。
具体修复步骤
1. 修正User结构体的字段可访问性
确保usermodels.User中用于存储用户名和用户ID的字段是首字母大写的可导出字段,并通过GORM标签明确对应数据库列:
package usermodels type User struct { UUID string `gorm:"column:uuid;primaryKey"` // 对应数据库uuid列 Name string `gorm:"column:user_name"` // 对应数据库user_name列 // 其他字段... } // 保持你的Protobuf()方法不变 func (u *User) Protobuf() *yourpb.User { // 结构体转Protobuf逻辑 }
同时修改原业务代码中的打印语句:
fmt.Printf("user name %+v", user.Name)
2. 对齐SQLMock的行数据与GORM查询
GORM执行db.First()时会查询结构体所有可导出字段对应的列,因此SQLMock的Rows必须包含这些列。至少需要包含查询条件用到的uuid和要打印的user_name:
rows := sqlmock.NewRows([]string{"uuid", "user_name"}). AddRow("user-fd3746c8-d32f-4fb8-8f6a-b6d72dcf2969", "John")
同时修正ExpectQuery中的SQL语句,确保与GORM生成的PostgreSQL语法完全匹配(注意列名的引号):
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "users" WHERE "uuid" = $1`)). WithArgs("user-fd3746c8-d32f-4fb8-8f6a-b6d72dcf2969"). WillReturnRows(rows)
3. 验证测试逻辑
在测试代码末尾添加SQLMock的预期验证,确保所有SQL操作都已执行:
if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("unfulfilled SQL expectations: %s", err) }
完整修正后的测试代码
func TestEnrollCourse(t *testing.T){ db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() rows := sqlmock.NewRows([]string{"uuid", "user_name"}). AddRow("user-fd3746c8-d32f-4fb8-8f6a-b6d72dcf2969", "John") mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "users" WHERE "uuid" = $1`)). WithArgs("user-fd3746c8-d32f-4fb8-8f6a-b6d72dcf2969"). WillReturnRows(rows) gdb, err := gorm.Open(postgres.New(postgres.Config{ Conn: db, }), &gorm.Config{}) if err != nil { t.Fatalf("failed to initialize GORM DB: %v", err) } err = enrollCourse(gdb, "user-fd3746c8-d32f-4fb8-8f6a-b6d72dcf2969", "english_course") if err != nil { t.Fatalf("enrollCourse execution failed: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("unfulfilled SQL expectations: %s", err) } }
内容的提问来源于stack exchange,提问作者Panduranga Rao Sadhu
相关产品推荐
相关产品推荐

