This plugin helps you to create objects that control data in your tests. These objects are called fixtures. They are used to create, and cleanup data in the database.
First of all you need to install the SQLc code generator. You can find the installation instructions here.
After that you need to install the plugin. You can do it by running the following command:
git clone [email protected]:debugger84/sqlc-fixture.git
cd sqlc-fixture
make all
Then you need to configure your sqlc.yaml file to use the plugin. You can find the configuration instructions here. For example, you can add the following lines to your sqlc.yaml file:
version: '2'
plugins:
- name: fixture
process:
## The path to the sqlc-fixture binary
cmd: "../sqlc-fixture/bin/sqlc-fixture"
- name: golang
## The path to the sqlc-gen-go wasm fork.
## This fork improves the names of the generated structs if they are in different PostgreSQL schemas.
## But you can use the original sqlc-gen-go wasmas well.
wasm:
url: "https://github.com/debugger84/sqlc-gen-go/releases/download/v1.3.1/sqlc-gen-go.wasm"
sha256: "fe6e5a2b75153ecba02b0c30bf4a11db2120bef537b650299473da133d272bf4"
sql:
- schema:
- "migration"
- "../types/migration"
queries: "queries"
engine: "postgresql"
codegen:
- plugin: fixture
## The path to the generated code. Should be the same as the path in the golang plugin.
out: "./"
options:
## The package name for the generated code.
## In this case the all fuxtures will be added to the subfolder "fixture"
## instead of storing in the same folder as other generated files.
package: "fixture"
## The package name for the generated by golang plugin models.
## Should be set up if the "package" option is configured previously.
model_import: "sqlc-gen-test/test"
## The primary keys columns that should be used in the fixtures.
## If the primary key is not named as "id" you need to specify it here.
primary_keys_columns:
- "user.name"
## All the next options should be the same as in the "golang" plugin.
sql_package: "pgx/v5"
default_schema: "test"
overrides:
- db_type: "uuid"
nullable: true
engine: "postgresql"
go_type:
import: "github.com/gofrs/uuid"
package: "uuid"
type: "NullUUID"
- db_type: "uuid"
nullable: false
engine: "postgresql"
go_type:
import: "github.com/gofrs/uuid"
package: "uuid"
type: "UUID"
- plugin: golang
out: "./"
options:
package: "test"
sql_package: "pgx/v5"
default_schema: "test"
exclude:
- "User.createdAt"
- "UpdateStatusInput.id"
## The same overrides as in the "fixture" plugin.
## If you wish to change some types, you need to do it in both plugins.
overrides:
- db_type: "uuid"
nullable: true
engine: "postgresql"
go_type:
import: "github.com/gofrs/uuid"
package: "uuid"
type: "NullUUID"
- db_type: "uuid"
nullable: false
engine: "postgresql"
go_type:
import: "github.com/gofrs/uuid"
package: "uuid"
type: "UUID"
After you have configured the plugin you can run the sqlc code generator as usual:
sqlc generate
The plugin will generate the fixtures for each table in the database. The fixtures will be stored in the subfolder "fixtures" in the package you have configured in the sqlc.yaml file.
For example, if you have the following table in the database:
CREATE TABLE users (
id uuid PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL
);
The plugin will generate the following fixture:
// Code generated by sqlc-fixture plugin for SQLc. DO NOT EDIT.
package fixture
import (
"context"
uuid "github.com/gofrs/uuid"
"sqlc-gen-test/test"
"testing"
)
type UserFixture struct {
entity test.User
db test.DBTX
}
func NewUserFixture(db test.DBTX, defaultEntity test.User) *UserFixture {
return &UserFixture{
db: db,
entity: defaultEntity,
}
}
func (f *UserFixture) ID(iD uuid.UUID) *UserFixture {
c := f.clone()
c.entity.ID = iD
return c
}
func (f *UserFixture) Name(name string) *UserFixture {
c := f.clone()
c.entity.Name = name
return c
}
func (f *UserFixture) Email(email string) *UserFixture {
c := f.clone()
c.entity.Email = email
return c
}
func (f *UserFixture) clone() *UserFixture {
return &UserFixture{
db: f.db,
entity: f.entity,
}
}
func (f *UserFixture) save(ctx context.Context) error {
query := `INSERT INTO users
(id, name, email)
VALUES ($1, $2, $3)
RETURNING id, name, email
`
row := f.db.QueryRow(ctx, query,
f.entity.ID,
f.entity.Name,
f.entity.Email,
)
err := row.Scan(
&f.entity.ID,
&f.entity.Name,
&f.entity.Email,
)
return err
}
func (f *UserFixture) GetEntity() test.User {
return f.entity
}
func (f *UserFixture) Create(tb testing.TB) *UserFixture {
err := f.save(context.Background())
if err != nil {
tb.Fatalf("failed to create User: %v", err)
}
f.Cleanup(tb)
c := f.clone()
return c
}
func (f *UserFixture) Cleanup(tb testing.TB) {
tb.Cleanup(
func() {
query := `DELETE FROM users WHERE id = $1`
_, err := f.db.Exec(context.Background(), query, f.entity.ID)
if err != nil {
tb.Fatalf("failed to cleanup User: %v", err)
}
})
}
func (f *UserFixture) PullUpdates(tb testing.TB) *UserFixture {
c := f.clone()
ctx := context.Background()
query := `SELECT * FROM users WHERE id = $1`
row := f.db.QueryRow(ctx, query,
c.entity.ID,
)
err := row.Scan(
&c.entity.ID,
&c.entity.Name,
&c.entity.Email,
)
if err != nil {
tb.Fatalf("failed to actualize data User: %v", err)
}
return c
}
You can use the fixtures in your tests as follows:
- Create the main test file where all tests dependencies will be initialized. Initialize the database connection and the fixtures for the tables you need to test.
package test_test
import (
"context"
"github.com/gofrs/uuid"
"github.com/jackc/pgx/v5"
"log"
"sqlc-gen-test/test"
"sqlc-gen-test/test/fixture"
"testing"
)
var testFixture *fixture.UserFixture
func TestMain(m *testing.M) {
// Initialize the database connection
ctx := context.Background()
db, err := pgx.Connect(ctx, "postgres://postgres:[email protected]:5432/test?sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close(ctx)
// Initialize the fixtures with the fields that the same in all tests of a package
testFixture = fixture.NewUserFixture(
db, test.User{
ID: uuid.Must(uuid.NewV4()),
},
)
m.Run()
}
- Write a test where you need to create a new user in the database.
package test_test
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestFixture(t *testing.T) {
// testFixture is initialized in the TestMain function
created := testFixture.
// Add fields to the fixture that are relevant for this test
// Don't worry. The fixture is developed as an immutable object.
// So setting the fields will create the cole of an object and not change the original fixture.
Email("[email protected]").
// Create the entity in the database
// After finishing the test, the entity will be deleted automatically
Create(t)
// Do any logic with the created entity inside tested code
//....
// Pull the data from the database to fixture
checkedEntity := created.PullUpdates(t).GetEntity()
assert.Equal(t, checkedEntity.Email, "[email protected]")
}