Golang中使用Mock时遇到常量相关错误的解决方案
Golang中使用Mock时遇到常量相关错误的解决方案 我遇到一个问题,我的函数接收一个结构体作为参数,该结构体包含 int64 类型的变量。
我使用了 go-mock 来模拟单元测试用例。
但问题是,当我传递 int64 变量时,它在函数级别(即参数层面)提示“Got”和“Want”不一致。
Running tool: /usr/local/go/bin/go test -timeout 30s -run ^Test_queryResolver_SellersByStatus$ query/gty_qry_bo/graph
=== RUN Test_queryResolver_SellersByStatus
=== RUN Test_queryResolver_SellersByStatus/TestCase_1
sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true
=== CONT Test_queryResolver_SellersByStatus
graph/schema.resolvers.go:56: Unexpected call to *mocks.MockBoQueryClient.GetSellersByStatus([context.Background sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true]) at gty_qry_bo/graph/schema.resolvers.go:56 because:
expected call at /gty_qry_bo/graph/internal_schema.resolvers_test.go:52 doesn't match the argument at index 1.
Got: sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true (*generated.SellersByStatusRequest)
Want: is equal to sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true (*generated.SellersByStatusRequest)
expected call at /graph/internal_schema.resolvers_test.go:64 doesn't match the argument at index 1.
Got: sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true (*generated.SellersByStatusRequest)
Want: is equal to sam_id:"2" status:"PFA" per_page:2 page_no:2 count:true (*generated.SellersByStatusRequest)
=== CONT Test_queryResolver_SellersByStatus/TestCase_1
/graph/testing.go:1169: test executed panic(nil) or runtime.Goexit: subtest may have called FailNow on a parent test
=== CONT Test_queryResolver_SellersByStatus
gty_qry_bo/graph/controller.go:137: missing call(s) to *mocks.MockBoQueryClient.GetSellersByStatus(is equal to context.Background (*context.emptyCtx), is equal to sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true (*generated.SellersByStatusRequest)) gty_qry_bo/graph/internal_schema.resolvers_test.go:52
gty_qry_bo/graph/controller.go:137: missing call(s) to *mocks.MockBoQueryClient.GetSellersByStatus(is equal to context.Background (*context.emptyCtx), is equal to sam_id:"2" status:"PFA" per_page:2 page_no:2 count:true (*generated.SellersByStatusRequest)) gty_qry_bo/graph/internal_schema.resolvers_test.go:64
gty_qry_bo/graph/controller.go:137: aborting test due to missing call(s)
--- FAIL: Test_queryResolver_SellersByStatus (0.00s)
--- FAIL: Test_queryResolver_SellersByStatus/TestCase_1 (0.00s)
FAIL
FAIL gateway_msvc/query/gty_qry_bo/graph 0.464s
但是,当我在 Recorder 函数中添加 fmt.Println(arg1) 时,测试用例就通过了。
fmt.Println 做了什么特殊处理吗?
Running tool: /usr/local/go/bin/go test -timeout 30s -run ^Test_queryResolver_SellersByStatus$ golang/gateway_msvc/query/gty_qry_bo/graph
=== RUN Test_queryResolver_SellersByStatus
sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true
sam_id:"2" status:"PFA" per_page:2 page_no:2 count:true
=== RUN Test_queryResolver_SellersByStatus/TestCase_1
sam_id:"2" status:"PFA" per_page:2 page_no:1 count:true
=== RUN Test_queryResolver_SellersByStatus/TestCase_2
sam_id:"2" status:"PFA" per_page:2 page_no:2 count:true
--- PASS: Test_queryResolver_SellersByStatus (0.00s)
--- PASS: Test_queryResolver_SellersByStatus/TestCase_1 (0.00s)
--- PASS: Test_queryResolver_SellersByStatus/TestCase_2 (0.00s)
PASS
ok golang/gateway_msvc/query/gty_qry_bo/graph 0.415s
更多关于Golang中使用Mock时遇到常量相关错误的解决方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你能分享一个实际的代码示例吗?我一时想不出任何理由,为什么 fmt.Print 系列函数中的任何一个会改变它们的参数,但如果你展示你的代码,我们可以提供更多信息。
更多关于Golang中使用Mock时遇到常量相关错误的解决方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
如果我将 arg1 的参数类型从 interface{} 改为我的结构体类型,它就能正常工作。
但是,如果我只是添加 fmt.Println() 而不改变参数类型,它是如何工作的呢?
通过修改“请勿编辑”文件来解决这个问题是否合适?
谢谢。
实际函数:-
// GetSellersByStatus indicates an expected call of GetSellersByStatus.
func (mr *MockBoQueryClientMockRecorder) GetSellersByStatus(arg0,arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSellersByStatus", reflect.TypeOf((*MockBoQueryClient)(nil).GetSellersByStatus), varargs...)
}
如果我更改参数类型或在不使用参数的情况下添加打印语句,它就能正常工作。 修改后的函数:-
// GetSellersByStatus indicates an expected call of GetSellersByStatus.
func (mr *MockBoQueryClientMockRecorder) GetSellersByStatus(arg0 interface{}, arg1 *generated.SellersByStatusRequest, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
//fmt.Println(arg1)
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSellersByStatus", reflect.TypeOf((*MockBoQueryClient)(nil).GetSellersByStatus), varargs...)
}
抱歉回复晚了
这个问题通常是由于GoMock在匹配结构体参数时,对结构体内部字段的比较方式导致的。特别是当结构体包含int64类型字段时,直接使用gomock.Eq()可能无法正确匹配。
问题根源在于GoMock的Eq()匹配器使用reflect.DeepEqual()进行深度比较,而int64类型的常量(如2)在传递给函数时,可能会被推断为不同的整数类型(如int),导致类型不匹配。
以下是解决方案和示例代码:
解决方案1:使用gomock.Eq()时确保类型一致
// 错误的写法
mockClient.EXPECT().GetSellersByStatus(
gomock.Any(),
&generated.SellersByStatusRequest{
SamId: "2",
Status: "PFA",
PerPage: 2, // 这里2是int类型,但结构体字段可能是int64
PageNo: 1,
Count: true,
},
).Return(&response, nil)
// 正确的写法 - 显式指定int64类型
mockClient.EXPECT().GetSellersByStatus(
gomock.Any(),
&generated.SellersByStatusRequest{
SamId: "2",
Status: "PFA",
PerPage: int64(2), // 显式转换为int64
PageNo: int64(1), // 显式转换为int64
Count: true,
},
).Return(&response, nil)
解决方案2:使用自定义匹配器
// 创建自定义匹配器
func matchSellersRequest(expected *generated.SellersByStatusRequest) gomock.Matcher {
return gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i interface{}) string {
return fmt.Sprintf("%v", i)
}),
gomock.WantFormatter(
gomock.StringerFunc(func() string {
return fmt.Sprintf("%v", expected)
}),
gomock.Eq(expected),
),
)
}
// 在测试中使用
mockClient.EXPECT().GetSellersByStatus(
gomock.Any(),
matchSellersRequest(&generated.SellersByStatusRequest{
SamId: "2",
Status: "PFA",
PerPage: 2,
PageNo: 1,
Count: true,
}),
).Return(&response, nil)
解决方案3:使用gomock.Any()忽略特定字段
mockClient.EXPECT().GetSellersByStatus(
gomock.Any(),
gomock.AssignableToTypeOf(&generated.SellersByStatusRequest{}),
).DoAndReturn(func(ctx context.Context, req *generated.SellersByStatusRequest) (*generated.SellersByStatusResponse, error) {
// 在Do函数中验证具体字段
if req.SamId != "2" || req.Status != "PFA" || req.PerPage != 2 || req.PageNo != 1 || !req.Count {
return nil, fmt.Errorf("unexpected request: %v", req)
}
return &response, nil
})
关于fmt.Println的作用
fmt.Println(arg1)能解决问题是因为:
- 强制类型推断:当调用
fmt.Println时,Go编译器需要确定参数的具体类型,这可能会改变常量类型的推断 - 内存布局变化:打印操作可能影响了结构体在内存中的对齐方式
- 编译器优化:添加打印语句可能改变了编译器的优化行为
但这只是掩盖了问题,不是真正的解决方案。正确的做法是确保测试代码中使用的类型与实际代码中的类型完全一致。
完整示例
func Test_queryResolver_SellersByStatus(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockClient := mocks.NewMockBoQueryClient(ctrl)
// 准备测试数据
expectedRequest1 := &generated.SellersByStatusRequest{
SamId: "2",
Status: "PFA",
PerPage: int64(2), // 关键:显式指定int64
PageNo: int64(1), // 关键:显式指定int64
Count: true,
}
expectedRequest2 := &generated.SellersByStatusRequest{
SamId: "2",
Status: "PFA",
PerPage: int64(2),
PageNo: int64(2),
Count: true,
}
// 设置mock期望
mockClient.EXPECT().GetSellersByStatus(
gomock.Any(),
expectedRequest1,
).Return(&generated.SellersByStatusResponse{
// 返回数据
}, nil)
mockClient.EXPECT().GetSellersByStatus(
gomock.Any(),
expectedRequest2,
).Return(&generated.SellersByStatusResponse{
// 返回数据
}, nil)
// 执行测试
// ...
}
关键点:确保测试代码中结构体字段的类型与生产代码中结构体定义的类型完全一致,特别是对于数值类型,要显式指定类型(如int64(2)而不是2)。

