Go Unit test – could not match actual sql with expected regexp while mocking gorm using go-sqlmock?

huangapple go评论109阅读模式
英文:

Go Unit test - could not match actual sql with expected regexp while mocking gorm using go-sqlmock?

问题

在进行单元测试时,我使用sqlmock来模拟gorm。但是当运行所有的初始设置都正常时,当实际查询和测试查询匹配时会出现错误。我在下面给出了所有的代码。

user.go

func (r *users) GetUserByID(userID uint) (*domain.User, *errors.RestErr) {
	var resp domain.User

	res := r.DB.Model(&domain.User{}).Where("id = ?", userID).First(&resp)

	if res.RowsAffected == 0 {
		logger.Error("获取用户失败", res.Error)
		return nil, errors.NewNotFoundError(errors.ErrRecordNotFound)
	}

	return &resp, nil
}

模拟

user_test.go

type Suite struct {
	suite.Suite
	DB         *gorm.DB
	mock       sqlmock.Sqlmock
	db         *sql.DB
	err        error
	repository repository.IUsers
}

func (s *Suite) SetupSuite() {
	s.db, s.mock, s.err = sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))

	require.NoError(s.T(), s.err)

	dialector := mysql.New(mysql.Config{
		DSN:                       "sqlmock_db_0",
		DriverName:                "mysql",
		Conn:                      s.db,
		SkipInitializeWithVersion: true,
	})

	s.DB, s.err = gorm.Open(dialector, &gorm.Config{})

	require.NoError(s.T(), s.err)
	s.repository = impl.NewMySqlUsersRepository(s.DB)
}

func (s *Suite) AfterTest(_, _ string) {
	require.NoError(s.T(), s.mock.ExpectationsWereMet())
}

func TestInit(t *testing.T) {
	suite.Run(t, new(Suite))
}

// .................开始测试....................

func (s *Suite) Test_repository_Get() {
	var (
		id        = uint(1)
		user_name = "user-name"
		fast_name = "fast-name"
	)

	s.mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "users" WHERE (id = $1) ORDER BY "users"."id" LIMIT 1`)).
		WithArgs(uint(id)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "user_name", "fast_name"}).
			AddRow(uint(id), user_name, fast_name))

	res, _ := s.repository.GetUserByID(uint(id))
	require.Nil(s.T(), deep.Equal(&domain.User{ID: uint(id), UserName: user_name, LastName: fast_name}, res))
}

当我执行res, _ := s.repository.GetUserByID(uint(id))这一行时,它会进入user.go文件。在sql查询行中出现错误,如下所示。

实际的sql语句: "SELECT * FROM `users` WHERE id = ? ORDER BY `users`.`id` LIMIT 1" 
与期望的"SELECT * FROM "users" WHERE (id = $1) ORDER BY "users"."id" LIMIT 1"不相等;

=> 期望的查询应该满足以下条件:
	- 匹配sql: 'SELECT * FROM "users" WHERE (id = $1) ORDER BY "users"."id" LIMIT 1'
	- 参数为:
		0 - 1
	- 应该返回结果:
		第0行 - [1 user-name fast-name]

这里有什么问题?

英文:

When unit testing I mock gorm using sqlmock. But when running all initial setup are fine but error happen when the actual query and testing query matches. I have given below all codes.

user.go

func (r *users) GetUserByID(userID uint) (*domain.User, *errors.RestErr) {
	var resp domain.User

	res := r.DB.Model(&domain.User{}).Where("id = ?", userID).First(&resp)

	if res.RowsAffected == 0 {
		logger.Error("error occurred when getting user by user id", res.Error)
		return nil, errors.NewNotFoundError(errors.ErrRecordNotFound)
	}

	return &resp, nil
}

> Mock

user_test.go

type Suite struct {
	suite.Suite
	DB         *gorm.DB
	mock       sqlmock.Sqlmock
	db         *sql.DB
	err        error
	repository repository.IUsers
}

func (s *Suite) SetupSuite() {
	s.db, s.mock, s.err = sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))

	require.NoError(s.T(), s.err)

	dialector := mysql.New(mysql.Config{
		DSN:                       "sqlmock_db_0",
		DriverName:                "mysql",
		Conn:                      s.db,
		SkipInitializeWithVersion: true,
	})

	s.DB, s.err = gorm.Open(dialector, &gorm.Config{})

	require.NoError(s.T(), s.err)
	s.repository = impl.NewMySqlUsersRepository(s.DB)
}

func (s *Suite) AfterTest(_, _ string) {
	require.NoError(s.T(), s.mock.ExpectationsWereMet())
}

func TestInit(t *testing.T) {
	suite.Run(t, new(Suite))
}

// .................Start Testing....................

func (s *Suite) Test_repository_Get() {
	var (
		id        = uint(1)
		user_name = "user-name"
		fast_name = "fast-name"
	)

	s.mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "users" WHERE (id = $1) ORDER BY "users"."id" LIMIT 1`)).
		WithArgs(uint(id)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "user_name", "fast_name"}).
			AddRow(uint(id), user_name, fast_name))

	res, _ := s.repository.GetUserByID(uint(id))
	require.Nil(s.T(), deep.Equal(&domain.User{ID: uint(id), UserName: user_name, LastName: fast_name}, res))
}

when I execute res, _ := s.repository.GetUserByID(uint(id)) this line it goes user.go file. Here in sql query line Getting error Like this given below .

actual sql: "SELECT * FROM `users` WHERE id = ? ORDER BY `users`.`id` LIMIT 1" 
does not equal to expected "SELECT \* FROM "users" WHERE \(id = \$1\) ORDER BY "users"\."id" LIMIT 1"


=> expecting Query, QueryContext or QueryRow which:
	 - matches sql: 'SELECT \* FROM "users" WHERE \(id = \$1\) ORDER BY "users"\."id" LIMIT 1'
	 - is with arguments:
				0 - 1
	 - should return rows:
				row 0 - [1 user-name fast-name]

What is the issue here?

答案1

得分: 2

主要问题是由于SQL查询引起的。对于Get-Item,我们不需要在这里使用完整的查询。Gorm会自动生成这个查询,只需要传递"SELECT(.*)"即可。

我在Medium的一篇博文中找到了这个信息。在这里你可以找到详细信息

下面的代码运行良好:

func (s *Suite) Test_repository_GetUserByID() {
    var (
        id         = uint(1)
        user_name  = "user-name"
        first_name = "first-name"
    )
    s.mock.ExpectQuery("SELECT(.*)").
        WithArgs(id).
        WillReturnRows(sqlmock.NewRows([]string{"id", "user_name", "first_name"}).
            AddRow(uint(id), user_name, first_name))

    res, _ := s.repository.GetUserByID(uint(id))
    require.Nil(s.T(), deep.Equal(&domain.User{ID: uint(id), UserName: user_name, FirstName: first_name}, res))
}
英文:

The main problem caused here the sql query. For Get-Item, we won't need full query here. Gorm automatically generate this. only need to pass

"SELECT(.*)"

I found this from a blog post on Medium.
Here you can find details

Bellow code works fine

func (s *Suite) Test_repository_GetUserByID() {
var (
	id         = uint(1)
	user_name  = "user-name"
	first_name = "first-name"
)
s.mock.ExpectQuery("SELECT(.*)").
	WithArgs(id).
	WillReturnRows(sqlmock.NewRows([]string{"id", "user_name", "first_name"}).
		AddRow(uint(id), user_name, first_name))

 res, _ := s.repository.GetUserByID(uint(id))
 require.Nil(s.T(), deep.Equal(&domain.User{ID: uint(id), UserName: user_name, FirstName: first_name}, res))
 }

huangapple
  • 本文由 发表于 2021年7月26日 18:55:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/68528616.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定