Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gorm (v1.22.4) problem #282

Open
ehduardu opened this issue Jan 19, 2022 · 4 comments
Open

gorm (v1.22.4) problem #282

ehduardu opened this issue Jan 19, 2022 · 4 comments

Comments

@ehduardu
Copy link

ehduardu commented Jan 19, 2022

Hello,

I am having a problem when I working with gorm v2. I have tried every solution and I could find but none worked.

gorm version: 1.22.4
sqlmock version: v1.5.0

This is my code:

package repositories

import (
	"balance-service/src/models"
	"database/sql"
	"log"
	"regexp"

	"github.com/DATA-DOG/go-sqlmock"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

var _ = Describe("Repository", func() {
	var repository *TableTests
	var mock sqlmock.Sqlmock

	BeforeEach(func() {
		var db *sql.DB
		var err error

		db, mock, err = sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
		Expect(err).ShouldNot(HaveOccurred())

		gdb, err := gorm.Open(postgres.New(postgres.Config{
			Conn: db,
		}), &gorm.Config{})
		Expect(err).ShouldNot(HaveOccurred())

		repository = NewTSRepository(gdb)
	})

	AfterEach(func() {
		err := mock.ExpectationsWereMet() // make sure all expectations were met
		Expect(err).ShouldNot(HaveOccurred())
	})

	Context("CreateSubscription", func() {
		var fakeSub *models.TableTest

		BeforeEach(func() {
			fakeSub = &models.TableTest{
				ID:   "123",
				Name: "dudu",
			}
		})

		It("save", func() {
			const query = `
					INSERT INTO "table_test" ("id","name")
						VALUES"`

			mock.MatchExpectationsInOrder(false)
			mock.ExpectBegin()
			mock.ExpectQuery(regexp.QuoteMeta(query)).
				WithArgs("123", "dudu").
				WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))

			mock.ExpectCommit()

			log.Println(repository)

			err := repository.CreateTS(fakeSub)
			Expect(err).ShouldNot(HaveOccurred())
		})
	})

})

The error:

Unexpected error:
      <*fmt.wrapError | 0xc00043fda0>: {
          msg: "call to ExecQuery 'INSERT INTO \"table_tests\" (\"id\",\"name\") VALUES ($1,$2)' with args [{Name: Ordinal:1 Value:123} {Name: Ordinal:2 Value:dudu}] was not expected; call to Rollback transaction was not expected",
          err: <*errors.errorString | 0xc00045dd60>{
              s: "call to Rollback transaction was not expected",
          },
      }
      call to ExecQuery 'INSERT INTO "table_tests" ("id","name") VALUES ($1,$2)' with args [{Name: Ordinal:1 Value:123} {Name: Ordinal:2 Value:dudu}] was not expected; call to Rollback transaction was not expected
  occurred

Thanks

@ehduardu ehduardu changed the title gorm v2 problem gorm (v1.22.4) problem Jan 19, 2022
@sunfuze
Copy link

sunfuze commented Feb 21, 2022

if you usesqlmock.QueryMatcherEqual, may be you should trim space around expect query

const query = `
					INSERT INTO "table_test" ("id","name")
						VALUES"`

to

const query = `INSERT INTO "table_test" ("id","name") VALUES"`

@sebastianbuechler
Copy link

Hello,

I am having a problem when I working with gorm v2. I have tried every solution and I could find but none worked.

gorm version: 1.22.4 sqlmock version: v1.5.0

This is my code:

package repositories

import (
	"balance-service/src/models"
	"database/sql"
	"log"
	"regexp"

	"github.com/DATA-DOG/go-sqlmock"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

var _ = Describe("Repository", func() {
	var repository *TableTests
	var mock sqlmock.Sqlmock

	BeforeEach(func() {
		var db *sql.DB
		var err error

		db, mock, err = sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
		Expect(err).ShouldNot(HaveOccurred())

		gdb, err := gorm.Open(postgres.New(postgres.Config{
			Conn: db,
		}), &gorm.Config{})
		Expect(err).ShouldNot(HaveOccurred())

		repository = NewTSRepository(gdb)
	})

	AfterEach(func() {
		err := mock.ExpectationsWereMet() // make sure all expectations were met
		Expect(err).ShouldNot(HaveOccurred())
	})

	Context("CreateSubscription", func() {
		var fakeSub *models.TableTest

		BeforeEach(func() {
			fakeSub = &models.TableTest{
				ID:   "123",
				Name: "dudu",
			}
		})

		It("save", func() {
			const query = `
					INSERT INTO "table_test" ("id","name")
						VALUES"`

			mock.MatchExpectationsInOrder(false)
			mock.ExpectBegin()
			mock.ExpectQuery(regexp.QuoteMeta(query)).
				WithArgs("123", "dudu").
				WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))

			mock.ExpectCommit()

			log.Println(repository)

			err := repository.CreateTS(fakeSub)
			Expect(err).ShouldNot(HaveOccurred())
		})
	})

})

The error:

Unexpected error:
      <*fmt.wrapError | 0xc00043fda0>: {
          msg: "call to ExecQuery 'INSERT INTO \"table_tests\" (\"id\",\"name\") VALUES ($1,$2)' with args [{Name: Ordinal:1 Value:123} {Name: Ordinal:2 Value:dudu}] was not expected; call to Rollback transaction was not expected",
          err: <*errors.errorString | 0xc00045dd60>{
              s: "call to Rollback transaction was not expected",
          },
      }
      call to ExecQuery 'INSERT INTO "table_tests" ("id","name") VALUES ($1,$2)' with args [{Name: Ordinal:1 Value:123} {Name: Ordinal:2 Value:dudu}] was not expected; call to Rollback transaction was not expected
  occurred

Thanks

I have the same issue. Did you find a solution yet?

Interestingly it seems to be an issue if all fields are provided into the create function or there's something like created_at which is set at DB level via defaults.

package gorm_test

import (
	"regexp"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Promoter struct {
	ID   uuid.UUID `json:"id" gorm:"type:uuid"`
	Name string    `json:"name"`
	// CreatedAt time.Time `json:"created_at" gorm:"default:CURRENT_TIMESTAMP"`
}

func TestDB(parentT *testing.T) {
	parentT.Parallel()

	sqlDB, mock, errSQLMock := sqlmock.New()
	assert.Nil(parentT, errSQLMock)

	db, errDB := gorm.Open(postgres.New(postgres.Config{Conn: sqlDB}), &gorm.Config{})
	assert.Nil(parentT, errDB)

	parentT.Run("Promoter", func(t *testing.T) {

		promoterID := uuid.New()
		name := "Promoter"

		// this expects the default transaction to begin
		mock.ExpectBegin()

		// this is the query to be expected
		mock.ExpectQuery(
			regexp.QuoteMeta(`INSERT INTO "promoters" ("id","name") VALUES ($1,$2)`)).
			WithArgs(promoterID, name).WillReturnRows(sqlmock.NewRows([]string{}))

		// this expects the default transaction to commit
		mock.ExpectCommit()

		promoter := Promoter{
			ID:   promoterID,
			Name: name,
		}

		err := db.Debug().Create(&promoter).Error
		assert.Nil(t, err)

		// ensure that all fields were set on the User object
		assert.Equal(t, promoter.ID, promoterID)
		assert.Equal(t, promoter.Name, name)

		// ensure that all expectations are met in the mock
		errExpectations := mock.ExpectationsWereMet()
		assert.Nil(t, errExpectations)
	})
}

Once I uncomment the

CreatedAt time.Time `json:"created_at" gorm:"default:CURRENT_TIMESTAMP"`

it works. And I don't know why that should change anything.

@dolmen
Copy link
Contributor

dolmen commented Apr 22, 2022

Please post some example code working on the Go Playground. Here is a template.

@j-dubb-dev
Copy link

Hello,
I am having a problem when I working with gorm v2. I have tried every solution and I could find but none worked.
gorm version: 1.22.4 sqlmock version: v1.5.0
This is my code:

package repositories

import (
	"balance-service/src/models"
	"database/sql"
	"log"
	"regexp"

	"github.com/DATA-DOG/go-sqlmock"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

var _ = Describe("Repository", func() {
	var repository *TableTests
	var mock sqlmock.Sqlmock

	BeforeEach(func() {
		var db *sql.DB
		var err error

		db, mock, err = sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
		Expect(err).ShouldNot(HaveOccurred())

		gdb, err := gorm.Open(postgres.New(postgres.Config{
			Conn: db,
		}), &gorm.Config{})
		Expect(err).ShouldNot(HaveOccurred())

		repository = NewTSRepository(gdb)
	})

	AfterEach(func() {
		err := mock.ExpectationsWereMet() // make sure all expectations were met
		Expect(err).ShouldNot(HaveOccurred())
	})

	Context("CreateSubscription", func() {
		var fakeSub *models.TableTest

		BeforeEach(func() {
			fakeSub = &models.TableTest{
				ID:   "123",
				Name: "dudu",
			}
		})

		It("save", func() {
			const query = `
					INSERT INTO "table_test" ("id","name")
						VALUES"`

			mock.MatchExpectationsInOrder(false)
			mock.ExpectBegin()
			mock.ExpectQuery(regexp.QuoteMeta(query)).
				WithArgs("123", "dudu").
				WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))

			mock.ExpectCommit()

			log.Println(repository)

			err := repository.CreateTS(fakeSub)
			Expect(err).ShouldNot(HaveOccurred())
		})
	})

})

The error:

Unexpected error:
      <*fmt.wrapError | 0xc00043fda0>: {
          msg: "call to ExecQuery 'INSERT INTO \"table_tests\" (\"id\",\"name\") VALUES ($1,$2)' with args [{Name: Ordinal:1 Value:123} {Name: Ordinal:2 Value:dudu}] was not expected; call to Rollback transaction was not expected",
          err: <*errors.errorString | 0xc00045dd60>{
              s: "call to Rollback transaction was not expected",
          },
      }
      call to ExecQuery 'INSERT INTO "table_tests" ("id","name") VALUES ($1,$2)' with args [{Name: Ordinal:1 Value:123} {Name: Ordinal:2 Value:dudu}] was not expected; call to Rollback transaction was not expected
  occurred

Thanks

I have the same issue. Did you find a solution yet?

Interestingly it seems to be an issue if all fields are provided into the create function or there's something like created_at which is set at DB level via defaults.

package gorm_test

import (
	"regexp"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Promoter struct {
	ID   uuid.UUID `json:"id" gorm:"type:uuid"`
	Name string    `json:"name"`
	// CreatedAt time.Time `json:"created_at" gorm:"default:CURRENT_TIMESTAMP"`
}

func TestDB(parentT *testing.T) {
	parentT.Parallel()

	sqlDB, mock, errSQLMock := sqlmock.New()
	assert.Nil(parentT, errSQLMock)

	db, errDB := gorm.Open(postgres.New(postgres.Config{Conn: sqlDB}), &gorm.Config{})
	assert.Nil(parentT, errDB)

	parentT.Run("Promoter", func(t *testing.T) {

		promoterID := uuid.New()
		name := "Promoter"

		// this expects the default transaction to begin
		mock.ExpectBegin()

		// this is the query to be expected
		mock.ExpectQuery(
			regexp.QuoteMeta(`INSERT INTO "promoters" ("id","name") VALUES ($1,$2)`)).
			WithArgs(promoterID, name).WillReturnRows(sqlmock.NewRows([]string{}))

		// this expects the default transaction to commit
		mock.ExpectCommit()

		promoter := Promoter{
			ID:   promoterID,
			Name: name,
		}

		err := db.Debug().Create(&promoter).Error
		assert.Nil(t, err)

		// ensure that all fields were set on the User object
		assert.Equal(t, promoter.ID, promoterID)
		assert.Equal(t, promoter.Name, name)

		// ensure that all expectations are met in the mock
		errExpectations := mock.ExpectationsWereMet()
		assert.Nil(t, errExpectations)
	})
}

Once I uncomment the

CreatedAt time.Time `json:"created_at" gorm:"default:CURRENT_TIMESTAMP"`

it works. And I don't know why that should change anything.

When you set defaults with the gorm struct tag, gorm does some things that you don't always expect. (https://github.com/go-gorm/gorm/blob/master/callbacks/create.go#L207-L295). The order of struct fields and the columns in the sql statement don't always line up, if the struct fields is nil it won't be passed as an argument to the query, and depending on your dialect it may change from an exec (requires sqlmock.ExpectExec) to a query (requires sqlmock.ExpectQuery) and the sql statement might have extra clauses added to it. (in sqlserver a default fields adds OUTPUT INSERTED."column_with_default_field" to the sql statement). If you're using the regex matcher, try using a broad match like mock.ExpectQuery(INSERT INTO "promoters" (.+) VALUES (.+)) WITHOUT the regexp.QuoteMeta (because (.+) literally matches on 1 or more of any character. If that matches then try mock.ExpectQuery(regexp.QuoteMeta(INSERT INTO "promoters" ("id","name") VALUES ($1,$2)))

Also, don't use regexp.QuoteMeta with the sqlmock.QueryMatcherEqual matcher. That could also be the reason because QuoteMeta with quote your ()'s and that will never match exactly with the actual query.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants