DATA-DOG / go-sqlmock

Sql mock driver for golang to test database interactions
Other
6.13k stars 407 forks source link

latest go-sqlmock(v1.3.3) and gorm(v1.9.11) version incompatible #200

Closed navono closed 5 years ago

navono commented 5 years ago

Use gorm-ut source with upgrade deps version, go.mod:

module github.com/Rosaniline/gorm-ut

go 1.13

require (
    github.com/DATA-DOG/go-sqlmock v1.3.3
    github.com/go-test/deep v1.0.4
    github.com/jinzhu/gorm v1.9.11
    github.com/satori/go.uuid v1.2.0
    github.com/stretchr/testify v1.4.0
)

and run test, command line shows:

?[35m(C:/Users/ping/Desktop/gorm-ut-master/pkg/repository/person.go:24)?[0m
?[33m[2019-10-21 15:52:57]?[0m ?[31;1m call to database transaction Begin, was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
  - matches sql: 'INSERT INTO "person" \("id","name"\)
                        VALUES \(\$1,\$2\) RETURNING "person"\."id"'
  - is with arguments:
    0 - c34a302e-bc5d-4e3d-8104-680ca2a438bb
    1 - test-name
  - should return rows:
    row 0 - [c34a302e-bc5d-4e3d-8104-680ca2a438bb] ?[0m

?[35m(C:/Users/ping/Desktop/gorm-ut-master/pkg/repository/person.go:30)?[0m
?[33m[2019-10-21 15:52:57]?[0m ?[31;1m Query: could not match actual sql: "SELECT * FROM "person" WHERE (id = ?)" with expected regexp "INSERT INTO "person" \("id","name"\) VALUES \(\$
1,\$2\) RETURNING "person"\."id"" ?[0m

?[35m(C:/Users/ping/Desktop/gorm-ut-master/pkg/repository/person.go:30)?[0m
?[33m[2019-10-21 15:52:57]?[0m  ?[36;1m[0.00ms]?[0m  SELECT * FROM "person"  WHERE (id = 'd5328d68-8664-4aab-ad92-8b74e3010ea8')
?[36;31m[0 rows affected or returned ]?[0m
--- FAIL: TestInit (0.01s)
    --- FAIL: TestInit/Test_repository_Create (0.01s)
        person_test.go:85:
                Error Trace:    person_test.go:85
                Error:          Received unexpected error:
                                call to database transaction Begin, was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
                                  - matches sql: 'INSERT INTO "person" \("id","name"\)
                                                        VALUES \(\$1,\$2\) RETURNING "person"\."id"'
                                  - is with arguments:
                                    0 - c34a302e-bc5d-4e3d-8104-680ca2a438bb
                                    1 - test-name
                                  - should return rows:
                                    row 0 - [c34a302e-bc5d-4e3d-8104-680ca2a438bb]
                Test:           TestInit/Test_repository_Create
        person_test.go:45:
                Error Trace:    person_test.go:45
                                                        suite.go:126
                                                        panic.go:563
                                                        testing.go:653
                                                        person_test.go:85
                Error:          Received unexpected error:
                                there is a remaining expectation which was not matched: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
                                  - matches sql: 'INSERT INTO "person" \("id","name"\)
                                                        VALUES \(\$1,\$2\) RETURNING "person"\."id"'
                                  - is with arguments:
                                    0 - c34a302e-bc5d-4e3d-8104-680ca2a438bb
                                    1 - test-name
                                  - should return rows:
                                    row 0 - [c34a302e-bc5d-4e3d-8104-680ca2a438bb]
                Test:           TestInit/Test_repository_Create
    --- FAIL: TestInit/Test_repository_Get (0.00s)
        person_test.go:66:
                Error Trace:    person_test.go:66
                Error:          Received unexpected error:
                                Query: could not match actual sql: "SELECT * FROM "person" WHERE (id = ?)" with expected regexp "INSERT INTO "person" \("id","name"\) VALUES \(\$1,\$2\)
 RETURNING "person"\."id""
                Test:           TestInit/Test_repository_Get
        person_test.go:45:
                Error Trace:    person_test.go:45
                                                        suite.go:126
                                                        panic.go:563
                                                        testing.go:653
                                                        person_test.go:66
                Error:          Received unexpected error:
                                there is a remaining expectation which was not matched: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
                                  - matches sql: 'INSERT INTO "person" \("id","name"\)
                                                        VALUES \(\$1,\$2\) RETURNING "person"\."id"'
                                  - is with arguments:
                                    0 - c34a302e-bc5d-4e3d-8104-680ca2a438bb
                                    1 - test-name
                                  - should return rows:
                                    row 0 - [c34a302e-bc5d-4e3d-8104-680ca2a438bb]
                Test:           TestInit/Test_repository_Get
FAIL
FAIL    github.com/Rosaniline/gorm-ut/pkg/repository    0.345s
FAIL

If not upgrade deps, keep it with origin repo, it works.

colachg commented 5 years ago

Yes,I have met this too.

colachg commented 5 years ago

@l3pp4rd Could you have a look at this?

navono commented 5 years ago

gorm@v1.9.2

// Begin start a transaction
func (scope *Scope) Begin() *Scope {
    if db, ok := scope.SQLDB().(sqlDb); ok {
        if tx, err := db.Begin(); err == nil {
            scope.db.db = interface{}(tx).(SQLCommon)
            scope.InstanceSet("gorm:started_transaction", true)
        }
    }
    return scope
}

and gorm@v1.9.11

// Begin start a transaction
func (scope *Scope) Begin() *Scope {
    if db, ok := scope.SQLDB().(sqlDb); ok {
        if tx, err := db.Begin(); scope.Err(err) == nil {
            scope.db.db = interface{}(tx).(SQLCommon)
            scope.InstanceSet("gorm:started_transaction", true)
        }
    }
    return scope
}

at 1.9.2, gorm not check db.Begin() error and 1.9.11 do, so the error saved to db.Error cause tests failed.

The main reasone is in sqlmock.begin(), the c.ordered always true and return error from it.


func (c *sqlmock) begin() (*ExpectedBegin, error) {
...
        if c.ordered {
            return nil, fmt.Errorf("call to database transaction Begin, was not expected, next expectation is: %s", next)
        }
    }
...
}
navono commented 5 years ago

After dig a little about sqlmock, I think sqlmock is fine. The right way is use MatchExpectationsInOrder to disable order and add ExpectBegin as this:

    s.mock.MatchExpectationsInOrder(false)
    s.mock.ExpectBegin()
    s.mock.ExpectQuery(regexp.QuoteMeta(
        `INSERT INTO "person" ("id","name") 
            VALUES ($1,$2) RETURNING "person"."id"`)).
        WithArgs(id, name).
        WillReturnRows(
            sqlmock.NewRows([]string{"id"}).AddRow(id.String()))
    s.mock.ExpectCommit()
navono commented 5 years ago

Another problem is that it doesn't support sqlite3

colachg commented 5 years ago

Mysql is not supported yet. this is the output when I replace "postgres" to "mysql" : call to ExecQuery 'INSERT INTOusers(id,name) VALUES (?,?)' with args [{Name: Ordinal:1 Value:1e2f940d-06e0-4c64-9fc3-5cbe81179280} {Name: Ordinal:2 Value:test-name}] was not expected

From the db log I find that:

Mysql execute sql is INSERT INTO `users` (`id`,`name`) VALUES ('1e2f940d-06e0-4c64-9fc3-5cbe81179280','test-name')

Postgresql execute sql is INSERT INTO "users" ("id","name") VALUES ('1e2f940d-06e0-4c64-9fc3-5cbe81179280','test-name') RETURNING "users"."id"

Below two "sqls" is different, I try to update regexp.QuoteMeta with escaping "`" but failed.

navono commented 5 years ago

Same as sqlite3. Another way is like this, but I haven't checked it yet.

rafareyes7 commented 4 years ago

Hi, I followed the @navono solution but I'm still having problem while trying to execute the INSERT, (with postgres driver) here is my code:

func TestRegister(t *testing.T) {
    qStr := `INSERT  INTO "customer" ("customer_key","first_name","middle_name","last_surname") 
    VALUES ($1,$2,$3,$4) RETURNING "customer"."customer_key"`

    t.Run("Must return the newly created customer key, if given customer input is valid", func(t *testing.T) {
        r := mocks.CustomerModel

        mock.MatchExpectationsInOrder(false)
        mock.ExpectBegin()
        mock.ExpectQuery(regexp.QuoteMeta(qStr)).
            WithArgs(r.CustomerKey, r.FirstName, r.MiddleName, r.LastSurname).
            WillReturnRows(sqlmock.NewRows([]string{"customer_key"}).AddRow(r.CustomerKey))
        mock.ExpectCommit()
        customerKey, err := repo.Register(r)

        assert.Nil(t, err)
        assert.NotNil(t, customerKey)
    })
}

and I get the following error:

Running tool: /usr/local/go/bin/go test -timeout 30s /internal/customer -run ^(TestRegister)$
call to Query 'INSERT  INTO "customer" ("customer_key","first_name","middle_name","last_surname",ationality_id","category_id") VALUES ($1,$2,$3,$4) RETURNING "customer"."customer_key"' with args [{Name: Ordinal:1 Value:RLKuxK-wnj8SUn50iFMjsrCtikTLTEmUFP7fOSE2veI=}
 {Name: Ordinal:2 Value:Martín} {Name: Ordinal:3 Value:Constan} 
{Name: Ordinal:4 Value:Smith}] 
was not expected <----------------------ERROR-------------------------

INSERT  INTO "customer" ("customer_key","first_name","middle_name","last_surname") VALUES ('RLKuxK-wnj8SUn50iFMjsrCtikTLTEmUFP7fOSE2veI=','Martín','','Constan','Smith'') RETURNING "customer"."customer_key"  
[0 rows affected or returned ]

(data-repository/internal/customer/repository.go:57) 
[2020-01-22 19:39:18]  call to Query 'INSERT INTO "customer" ("customer_key","first_name","middle_name","last_surname",ationality_id","category_id") VALUES ($1,$2,$3,$4) RETURNING "customer"."customer_key"' with args [{Name: Ordinal:1 Value:RLKuxK-wnj8SUn50iFMjsrCtikTLTEmUFP7fOSE2veI=} {Name: Ordinal:2 Value:Martín} {Name: Ordinal:3 Value:Constan} {Name: Ordinal:4 Value:Smith}] 
was not expected <----------------------ERROR AGAIN-------------------------

--- FAIL: TestRegister (0.00s)
    --- FAIL: TestRegister/Must_return_the_newly_created_customer_key,_if_given_customer_input_is_valid (0.00s)
        data-repository/internal/customer/repository_test.go:89: 
                Error Trace:    repository_test.go:89
                Error:          Expected nil, but got: &status.statusError{Code:13, Message:"An error occurred.", Details:[]*any.Any{(*any.Any)(0xc000215f90)}, XXX_NoUnkeyedLiteral:struct {}{}, XXX_unrecognized:[]uint8(nil), XXX_sizecache:0}
                Test:           TestRegister/Must_return_the_newly_created_customer_key,_if_given_customer_input_is_valid
FAIL
FAIL    /internal/customer  0.008s
FAIL
Error: Tests failed.

I'm using the following versions:

github.com/DATA-DOG/go-sqlmock v1.4.0 github.com/jinzhu/gorm v1.9.11

rafareyes7 commented 4 years ago

Hi @navono, would you check this issue please #215

davidxiao commented 4 years ago

Hi, @navono , I am having the same issue, and tried to the solution you mentioned, while it doesn't work for me, but the message changed to the below, call to database transaction Begin was not expected I am guessing it's related to gorm?, so opened a ticket there,https://github.com/jinzhu/gorm/issues/2965 Can you pls give any advice to this issue? thanks