英文:
Go mockgen, mocked function not called
问题
我正在使用具有8个核心的 Windows 10 Pro 操作系统上的 Windows 机器上的 Go 1.19 版本。
我使用了 mockgen 工具来生成模拟对象。当我执行 EXPECT() 函数时,我可以看到模拟方法被记录下来。
模拟函数被调用,但测试失败并显示“缺少调用”。
我无法看出我做错了什么,请问有人可以指出问题吗?
目录结构:
cmd
configure.go
configure_test.go
mocks
mock_validator.go
validator
validator.go
user
user.go
go.mod
main.go
* main.go 的内容
package main
import (
"localdev/mockexample/cmd"
)
func main() {
cmd.Configure()
}
* configure.go 的内容
package cmd
import (
"fmt"
"localdev/mockexample/user"
"os"
"localdev/mockexample/validator"
)
var (
name, password string
)
func Configure() {
name := os.Args[1]
password := os.Args[2]
user, err := validate(validator.NewValidator(name, password))
if err != nil {
fmt.Printf("%v\n", err)
return
}
fmt.Printf("Credentials are valid. Welcome: %s %s\n", user.FirstName, user.LastName)
}
func validate(validator validator.Validator) (*user.Data, error) {
user, err := validator.ValidateUser()
if err != nil {
return nil, fmt.Errorf("something went wrong. %v", err)
}
return user, nil
}
* validator.go 的内容
package validator
import (
"fmt"
"localdev/mockexample/user"
)
//go:generate mockgen -destination=../mocks/mock_validator.go -package=mocks localdev/mockexample/validator Validator
type Validator interface {
ValidateUser() (*user.Data, error)
}
type ValidationRequest struct {
Command string
Name string
Password string
}
func (vr ValidationRequest) ValidateUser() (*user.Data, error) {
if vr.Name == "bob" && vr.Password == "1234" {
return &user.Data{UserID: "123", UserName: "bsmith", FirstName: "Bob", LastName: "Smith"}, nil
}
return nil, fmt.Errorf("invalid credentials")
}
func NewValidator(name string, password string) Validator {
return &ValidationRequest{Name: name, Password: password}
}
* user.go 的内容
package user
type Data struct {
UserID string `json:"user_id"`
UserName string `json:"user_name"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
* configure_test.go 的内容
package cmd
import (
"localdev/mockexample/mocks"
"localdev/mockexample/user"
"os"
"testing"
"github.com/golang/mock/gomock"
)
func TestConfigure(t *testing.T) {
t.Run("ConfigureWithMock", func(t *testing.T) {
os.Args[1] = "bob"
os.Args[2] = "1234"
ctrl := gomock.NewController(t)
mockValidator := mocks.NewMockValidator(ctrl)
//mockValidator.EXPECT().ValidateUser().AnyTimes() // zero more calls, so this will also pass.
userData := user.Data{UserID: "testId"}
mockValidator.EXPECT().ValidateUser().Return(&userData, nil).Times(1) //(gomock.Any(), gomock.Any()) //(&userData, nil)
Configure()
})
}
生成的模拟对象的内容
// Code generated by MockGen. DO NOT EDIT.
// Source: localdev/mockexample/validator (interfaces: Validator)
// Package mocks is a generated GoMock package.
package mocks
import (
user "localdev/mockexample/user"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockValidator is a mock of Validator interface.
type MockValidator struct {
ctrl *gomock.Controller
recorder *MockValidatorMockRecorder
}
// MockValidatorMockRecorder is the mock recorder for MockValidator.
type MockValidatorMockRecorder struct {
mock *MockValidator
}
// NewMockValidator creates a new mock instance.
func NewMockValidator(ctrl *gomock.Controller) *MockValidator {
mock := &MockValidator{ctrl: ctrl}
mock.recorder = &MockValidatorMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockValidator) EXPECT() *MockValidatorMockRecorder {
return m.recorder
}
// ValidateUser mocks base method.
func (m *MockValidator) ValidateUser() (*user.Data, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateUser")
ret0, _ := ret[0].(*user.Data)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ValidateUser indicates an expected call of ValidateUser.
func (mr *MockValidatorMockRecorder) ValidateUser() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUser", reflect.TypeOf((*MockValidator)(nil).ValidateUser))
}
英文:
I am using Go 1.19 on a windows machine with 8 cores, operating system is Windows 10 Pro.
I used the mockgen tool to generate the mock. When I debug my test I see the mocked method is recorded when I execute the EXPECT() function.
The mocked function is called, but the test fails with 'missing call' on the mocked function.
I cannot see what I am doing wrong, can anyone please point it out ?
Directory Structure :
cmd
configure.go
configure_test.go
mocks
mock_validator.go
validator
validator.go
user
user.go
go.mod
main.go
* Contents of main.go
package main
import (
"localdev/mockexample/cmd"
)
func main() {
cmd.Configure()
}
* Contents of configure.go
package cmd
import (
"fmt"
"localdev/mockexample/user"
"os"
"localdev/mockexample/validator"
)
var (
name, password string
)
func Configure() {
name := os.Args[1]
password := os.Args[2]
user, err := validate(validator.NewValidator(name, password))
if err != nil {
fmt.Printf("%v\n", err)
return
}
fmt.Printf("Credentials are valid. Welcome: %s %s\n", user.FirstName, user.LastName)
}
func validate(validator validator.Validator) (*user.Data, error) {
user, err := validator.ValidateUser()
if err != nil {
return nil, fmt.Errorf("some thing went wrong. %v", err)
}
return user, nil
}
* Contents of validator.go
package validator
import (
"fmt"
"localdev/mockexample/user"
)
//go:generate mockgen -destination=../mocks/mock_validator.go -package=mocks localdev/mockexample/validator Validator
type Validator interface {
ValidateUser() (*user.Data, error)
}
type ValidationRequest struct {
Command string
Name string
Password string
}
func (vr ValidationRequest) ValidateUser() (*user.Data, error) {
if vr.Name == "bob" && vr.Password == "1234" {
return &user.Data{UserID: "123", UserName: "bsmith", FirstName: "Bob", LastName: "Smith"}, nil
}
return nil, fmt.Errorf("invalid credentials")
}
func NewValidator(name string, password string) Validator {
return &ValidationRequest{Name: name, Password: password}
}
* Contents of user.go
package user
type Data struct {
UserID string `json:"user_id"`
UserName string `json:"user_name"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
* Contents of configure_test.go
package cmd
import (
"localdev/mockexample/mocks"
"localdev/mockexample/user"
"os"
"testing"
"github.com/golang/mock/gomock"
)
func TestConfigure(t *testing.T) {
t.Run("ConfigureWithMock", func(t *testing.T) {
os.Args[1] = "bob"
os.Args[2] = "1234"
ctrl := gomock.NewController(t)
mockValidator := mocks.NewMockValidator(ctrl)
//mockValidator.EXPECT().ValidateUser().AnyTimes() // zero more calls, so this will also pass.
userData := user.Data{UserID: "testId"}
mockValidator.EXPECT().ValidateUser().Return(&userData, nil).Times(1) //(gomock.Any(), gomock.Any()) //(&userData, nil)
Configure()
})
}
Contents of generated mock
// Code generated by MockGen. DO NOT EDIT.
// Source: localdev/mockexample/validator (interfaces: Validator)
// Package mocks is a generated GoMock package.
package mocks
import (
user "localdev/mockexample/user"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockValidator is a mock of Validator interface.
type MockValidator struct {
ctrl *gomock.Controller
recorder *MockValidatorMockRecorder
}
// MockValidatorMockRecorder is the mock recorder for MockValidator.
type MockValidatorMockRecorder struct {
mock *MockValidator
}
// NewMockValidator creates a new mock instance.
func NewMockValidator(ctrl *gomock.Controller) *MockValidator {
mock := &MockValidator{ctrl: ctrl}
mock.recorder = &MockValidatorMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockValidator) EXPECT() *MockValidatorMockRecorder {
return m.recorder
}
// ValidateUser mocks base method.
func (m *MockValidator) ValidateUser() (*user.Data, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateUser")
ret0, _ := ret[0].(*user.Data)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ValidateUser indicates an expected call of ValidateUser.
func (mr *MockValidatorMockRecorder) ValidateUser() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUser", reflect.TypeOf((*MockValidator)(nil).ValidateUser))
}
答案1
得分: 1
根本问题在于函数Configure
从未使用过模拟结构,所以会出现missing call(s) to *mocks.MockValidator.ValidateUser()
的错误。
在文件configure_test.go
中,根本没有使用mockValidator
。必须对该模拟进行某种注入,以便被Configure
函数调用。
你可以进行以下更改来修复测试,这只是一个示例,说明我所说的注入。并不是说这是最佳方法,而是尽量对代码进行最少的更改。
configure_test.go
:
func TestConfigure(t *testing.T) {
t.Run("ConfigureWithMock", func(t *testing.T) {
os.Args[1] = "bob"
os.Args[2] = "1234"
ctrl := gomock.NewController(t)
mockValidator := mocks.NewMockValidator(ctrl)
//mockValidator.EXPECT().ValidateUser().AnyTimes() // zero more calls, so this will also pass.
userData := user.Data{UserID: "testId"}
mockValidator.
EXPECT().
ValidateUser("bob", "1234").
Return(&userData, nil).
Times(1) //(gomock.Any(), gomock.Any()) //(&userData, nil)
Configure(mockValidator)
})
}
configure.go
func Configure(v validator.Validator) {
name := os.Args[1]
password := os.Args[2]
user, err := v.ValidateUser(name, password)
if err != nil {
fmt.Printf("something went wrong. %v\n", err)
return
}
fmt.Printf("Credentials are valid. Welcome: %s %s\n", user.FirstName, user.LastName)
}
validator.go
type Validator interface {
ValidateUser(name, password string) (*user.Data, error)
}
type ValidationRequest struct {
Command string
// Name string
// Password string
}
func (vr ValidationRequest) ValidateUser(name, password string) (*user.Data, error) {
if name == "bob" && password == "1234" {
return &user.Data{UserID: "123", UserName: "bsmith", FirstName: "Bob", LastName: "Smith"}, nil
}
return nil, fmt.Errorf("invalid credentials")
}
func NewValidator() Validator {
return &ValidationRequest{}
}
请注意,你需要重新生成模拟。希望这能帮助你理解模拟测试。
英文:
The root problem is that the function Configure
never uses the mock structure, so you get a missing call(s) to *mocks.MockValidator.ValidateUser()
error.
In the file configure_test.go
, mockValidator
is simply not used at all. There must be some kind of injection of that mock in order to be called by the Configure
function.
You could make the following changes to fix the test, as an example of what I'm referring to injection. Not saying this is the best approach but I'm trying to make the fewer possible changes to your code.
configure_test.go
:
func TestConfigure(t *testing.T) {
t.Run("ConfigureWithMock", func(t *testing.T) {
os.Args[1] = "bob"
os.Args[2] = "1234"
ctrl := gomock.NewController(t)
mockValidator := mocks.NewMockValidator(ctrl)
//mockValidator.EXPECT().ValidateUser().AnyTimes() // zero more calls, so this will also pass.
userData := user.Data{UserID: "testId"}
mockValidator.
EXPECT().
ValidateUser("bob", "1234").
Return(&userData, nil).
Times(1) //(gomock.Any(), gomock.Any()) //(&userData, nil)
Configure(mockValidator)
})
}
configure.go
func Configure(v validator.Validator) {
name := os.Args[1]
password := os.Args[2]
user, err := v.ValidateUser(name, password)
if err != nil {
fmt.Printf("some thing went wrong. %v\n", err)
return
}
fmt.Printf("Credentials are valid. Welcome: %s %s\n", user.FirstName, user.LastName)
}
validator.go
type Validator interface {
ValidateUser(name, password string) (*user.Data, error)
}
type ValidationRequest struct {
Command string
// Name string
// Password string
}
func (vr ValidationRequest) ValidateUser(name, password string) (*user.Data, error) {
if name == "bob" && password == "1234" {
return &user.Data{UserID: "123", UserName: "bsmith", FirstName: "Bob", LastName: "Smith"}, nil
}
return nil, fmt.Errorf("invalid credentials")
}
func NewValidator() Validator {
return &ValidationRequest{}
}
Take into account that you need to generate the mock again. Hope this helps you to understand mock testing.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论