Search code examples
gotestinggo-gormgo-sqlmock

How to mock gorm insert with go-sql (postgres)


I'm using Gorm with the postgresql driver. I try to mock a database insert with go-sqlmock:

type Test struct {
    FirstName string `json:"first_name"`
}

func (db *DB) CreateTest() (*Test, error) {
    test := Test{"c"}
    if result := db.Create(&test); result.Error != nil {
        return nil, result.Error
    }

    return &test, nil
}


func TestCreateUser(t *testing.T) {
    _, mock, _ := sqlmock.NewWithDSN("sqlmock_db_0")

    mockedGorm, _ := gorm.Open("sqlmock", "sqlmock_db_0")
    defer mockedGorm.Close()
    myDB := &DB{mockedGorm}

    mock.ExpectExec("INSERT INTO test").WithArgs("c").WillReturnResult(sqlmock.NewResult(1, 1))
    myDB.Exec("INSERT INTO test(first_name) VALUES (?)", "c")


    if _, err := myDB.CreateTest(); err != nil {
        t.Errorf("error was not expected: %s", err)
    }

    if err := mock.ExpectationsWereMet(); err != nil {
        t.Errorf("there were unfulfilled expectations: %s", err)
    }
}

Unfortunately, this gives me an error:

error was not expected: all expectations were already fulfilled, call to database transaction Begin was not expected

How do I test an insert with gorm, postgresql and sql-mock properly?


Solution

  • There were a couple of issues with my code:

    1) As @flimzy pointed out rightfully, there has to be a ExpectBegin() (and ExpectCommit()) statement. This gets more obvious if one turns on the GORM debugger that shows what exactly GORM is doing.

    2) ExpectExec("INSERT INTO test").WithArgs("c") does quite obviously not match myDB.Exec("INSERT INTO test(first_name) VALUES (?)", "c")

    3) One has to escape the statement, as go-sqlmock takes a regex, here Go's https://godoc.org/regexp#QuoteMeta comes in handy.

    Working code:

    mock.ExpectBegin()
    mock.ExpectExec(regexp.QuoteMeta("INSERT INTO \"tests\" (\"first_name\") VALUES (?)")).WithArgs("c").WillReturnResult(sqlmock.NewResult(1, 1))
    mock.ExpectCommit()