A lightweight SQL mock driver for Go that helps you test code using database/sql without a real database connection.
This package provides:
- Expectation-based query and exec matching.
- Simple argument matching, including wildcard support via
AnyArg(). - In-memory rows builder for deterministic query results.
- Final expectation verification with
ExpectationsWereMet().
go get github.com/sembraniteam/sqlmockpackage main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
func GetUserNameByID(ctx context.Context, db *sql.DB, id int64) (string, error) {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return "", err
}
defer tx.Rollback()
var name string
err = tx.QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", id).Scan(&name)
if err != nil {
return "", err
}
if err := tx.Commit(); err != nil {
return "", err
}
return name, nil
}
func main() {
db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/app_db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatal(err)
}
name, err := GetUserNameByID(context.Background(), db, 1)
if err != nil {
log.Fatal(err)
}
fmt.Println("user name:", name)
}package main
import (
"context"
"testing"
"github.com/sembraniteam/sqlmock"
"github.com/stretchr/testify/require"
)
func TestGetUserNameByID(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
mock.ExpectBegin()
rows := sqlmock.NewRows([]string{"name"}).AddRows("Alice")
mock.ExpectQuery("SELECT name FROM users WHERE id = ?").
WithArgs(int64(1)).
WillReturnRows(rows)
mock.ExpectCommit()
name, err := GetUserNameByID(context.Background(), db, 1)
require.NoError(t, err)
require.Equal(t, "Alice", name)
require.NoError(t, mock.ExpectationsWereMet())
}By default, sqlmock.New() uses non-strict transaction mode, so query/exec can run without Begin/Commit.
package main
import (
"database/sql"
)
func GetUserNameByIDNoTx(db *sql.DB, id int64) (string, error) {
var name string
err := db.QueryRow("SELECT name FROM users WHERE id = ?", id).Scan(&name)
if err != nil {
return "", err
}
return name, nil
}package main
import (
"testing"
"github.com/sembraniteam/sqlmock"
"github.com/stretchr/testify/require"
)
func TestGetUserNameByIDNoTx(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
rows := sqlmock.NewRows([]string{"name"}).AddRows("Alice")
mock.ExpectQuery("SELECT name FROM users WHERE id = ?").
WithArgs(int64(1)).
WillReturnRows(rows)
name, err := GetUserNameByIDNoTx(db, 1)
require.NoError(t, err)
require.Equal(t, "Alice", name)
require.NoError(t, mock.ExpectationsWereMet())
}Use WithStrictTx() when you want tests to fail if query/exec is executed outside a transaction.
package main
import (
"context"
"testing"
"github.com/sembraniteam/sqlmock"
"github.com/stretchr/testify/require"
)
func TestGetUserNameByID_StrictTx(t *testing.T) {
db, mock, err := sqlmock.NewWithOptions(sqlmock.WithStrictTx())
require.NoError(t, err)
defer db.Close()
// Query without Begin should fail in strict transaction mode.
_, err = db.Query("SELECT name FROM users WHERE id = ?", int64(1))
require.Error(t, err)
require.ErrorContains(t, err, "query without active transaction")
// Proper transactional flow.
mock.ExpectBegin()
mock.ExpectQuery("SELECT name FROM users WHERE id = ?").
WithArgs(int64(1)).
WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRows("Alice"))
mock.ExpectCommit()
tx, err := db.BeginTx(context.Background(), nil)
require.NoError(t, err)
var name string
err = tx.QueryRow("SELECT name FROM users WHERE id = ?", int64(1)).Scan(&name)
require.NoError(t, err)
require.Equal(t, "Alice", name)
require.NoError(t, tx.Commit())
require.NoError(t, mock.ExpectationsWereMet())
}Use AnyArg() or AnyArgOf[T]() when the exact argument value is not important:
mock.ExpectExec("INSERT INTO sessions (id, created_at, updated_at) VALUES (?, ?, ?)").
WithArgs(
int64(1),
sqlmock.AnyArg(),
sqlmock.AnyArgOf[int64](),
).
WillReturnResult(1)