Golang Unit Testing with Gorm and Sqlmock PostgreSQL — Simplest setup
In this tutorial I will highlight the Gorm and mock SQL part which I believe is the hard part.
There will be no unit test assert
here to make it short and concise.
Simplest Gorm INSERT and test case
Here is the simple product file
package product
import (
"gorm.io/gorm"
)
type Product struct {
Code string
Price uint
}
func createProduct(db *gorm.DB) {
db.Create(&Product{Code: "D42", Price: 100})
}
Then we have the test file
package product
import (
"testing"
"github.com/DATA-DOG/go-sqlmock"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func TestCreateProduct(t *testing.T) {
mockDb, mock, _ := sqlmock.New()
dialector := postgres.New(postgres.Config{
Conn: mockDb,
DriverName: "postgres",
})
db, _ := gorm.Open(dialector, &gorm.Config{})
// CASE 1
createProduct(db)
// CASE 2
// fmt.Println(mock)
rows := sqlmock.NewRows([]string{"Code", "Price"}).AddRow("D43", 100)
mock.ExpectQuery(`SELECT`).WillReturnRows(rows)
createProduct(db)
}
Then we run the test
go test -cover
Got the pass result.
The key here to mock the database is sqlmock.New()
this will represent the mock database and interact with Gorm.
You can extend the result to assert create success by sqlmock.ExpectExec
with return value (1, 1)
to asserted.
More on Querying
In more realistic case, we might need to check something before create like check the existing Code
, now the mock
object become more useful
package product
import (
"gorm.io/gorm"
)
type Product struct {
Code string
Price uint
}
func createProduct(db *gorm.DB) {
var product Product
err := db.First(&product, "code = ?", "D42").Error
if err != nil {
return
}
db.Create(&Product{Code: "D42", Price: 100})
}
Then our test file will add the mock.ExpectQuery
package product
import (
"testing"
"github.com/DATA-DOG/go-sqlmock"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func TestCreateProduct(t *testing.T) {
mockDb, mock, _ := sqlmock.New()
dialector := postgres.New(postgres.Config{
Conn: mockDb,
DriverName: "postgres",
})
db, _ := gorm.Open(dialector, &gorm.Config{})
// CASE 1
createProduct(db)
// CASE 2
// fmt.Println(mock)
rows := sqlmock.NewRows([]string{"Code", "Price"}).AddRow("D43", 100)
mock.ExpectQuery(`SELECT`).WillReturnRows(rows)
createProduct(db)
}
Note the this isRegExp
so I just use theSELECT
statement
Also note that there is branching:
- If function found
code = ?
then it return - Function run through the end and create the record
So I add two case to make the coverage become 100%
go test -cover
PASS
product coverage: 100.0% of statements
ok product 0.004s
Common mistake
testing: warning: no tests to run
You need the TestNameFunction
to begin with capital letter.
Hope this help and see you next time