ce5be947b4
Revert fixture fixes for postgres Use postgres connection string with spaces instead of url Fix label order Make postgres tests in ci less verbose Add sequence update script Skip resets in postgres Remove option to skip resets in postgres Make postgres tests in ci verboseq Update test fixtures database Fix file tests on postgres Add postgres options to sample config Make sure tests init test fixtures before running the actual tests Fix issues with IDs too big to fit in an int Fix duplicate auto incremented IDs Refactor / Fix team tests Refactor team member tests Fix team member create Fix label test Fix getting labels Fix test fixtures for postgresql Fix connection string params Disable ssl mode on postgres integration tests Disable ssl mode on postgres tests Use sprintf to create the connection string for postgresql fixup! Add postgres support Add postgres support Added generate as a make dependency for make build Clarify docs on building Co-authored-by: kolaente <k@knt.li> Co-authored-by: Jan Tojnar <jtojnar@gmail.com> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/135
464 lines
12 KiB
Markdown
464 lines
12 KiB
Markdown
# testfixtures
|
|
|
|
[![GoDoc](https://godoc.org/github.com/go-testfixtures/testfixtures?status.svg)][doc]
|
|
|
|
> ***Warning***: this package will wipe the database data before loading the
|
|
fixtures! It is supposed to be used on a test database. Please, double check
|
|
if you are running it against the correct database.
|
|
|
|
> **TIP**: There are options not described in this README page. It's
|
|
> recommended that you also check [the documentation][doc].
|
|
|
|
Writing tests is hard, even more when you have to deal with an SQL database.
|
|
This package aims to make writing functional tests for web apps written in
|
|
Go easier.
|
|
|
|
Basically this package mimics the ["Ruby on Rails' way"][railstests] of writing tests
|
|
for database applications, where sample data is kept in fixtures files. Before
|
|
the execution of every test, the test database is cleaned and the fixture data
|
|
is loaded into the database.
|
|
|
|
The idea is running tests against a real database, instead of relying in mocks,
|
|
which is boring to setup and may lead to production bugs not being caught in
|
|
the tests.
|
|
|
|
## Installation
|
|
|
|
First, import it like this:
|
|
|
|
```go
|
|
import (
|
|
"github.com/go-testfixtures/testfixtures/v3"
|
|
)
|
|
```
|
|
|
|
## Usage
|
|
|
|
Create a folder for the fixture files. Each file should contain data for a
|
|
single table and have the name `<table_name>.yml`:
|
|
|
|
```
|
|
myapp/
|
|
myapp.go
|
|
myapp_test.go
|
|
...
|
|
fixtures/
|
|
posts.yml
|
|
comments.yml
|
|
tags.yml
|
|
posts_tags.yml
|
|
...
|
|
```
|
|
|
|
The file would look like this (it can have as many record you want):
|
|
|
|
```yml
|
|
# comments.yml
|
|
- id: 1
|
|
post_id: 1
|
|
content: A comment...
|
|
author_name: John Doe
|
|
author_email: john@doe.com
|
|
created_at: 2020-12-31 23:59:59
|
|
updated_at: 2020-12-31 23:59:59
|
|
|
|
- id: 2
|
|
post_id: 2
|
|
content: Another comment...
|
|
author_name: John Doe
|
|
author_email: john@doe.com
|
|
created_at: 2020-12-31 23:59:59
|
|
updated_at: 2020-12-31 23:59:59
|
|
|
|
# ...
|
|
```
|
|
|
|
An YAML object or array will be converted to JSON. It will be stored on a native
|
|
JSON type like JSONB on PostgreSQL or as a TEXT or VARCHAR column on other
|
|
databases.
|
|
|
|
```yml
|
|
- id: 1
|
|
post_attributes:
|
|
author: John Due
|
|
author_email: john@due.com
|
|
title: "..."
|
|
tags:
|
|
- programming
|
|
- go
|
|
- testing
|
|
post: "..."
|
|
```
|
|
|
|
If you need to write raw SQL, probably to call a function, prefix the value
|
|
of the column with `RAW=`:
|
|
|
|
```yml
|
|
- id: 1
|
|
uuid_column: RAW=uuid_generate_v4()
|
|
postgis_type_column: RAW=ST_GeomFromText('params...')
|
|
created_at: RAW=NOW()
|
|
updated_at: RAW=NOW()
|
|
```
|
|
|
|
Your tests would look like this:
|
|
|
|
```go
|
|
package myapp
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
_ "github.com/lib/pq"
|
|
"github.com/go-testfixtures/testfixtures/v3"
|
|
)
|
|
|
|
var (
|
|
db *sql.DB
|
|
fixtures *testfixtures.Loader
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
var err error
|
|
|
|
// Open connection to the test database.
|
|
// Do NOT import fixtures in a production database!
|
|
// Existing data would be deleted.
|
|
db, err = sql.Open("postgres", "dbname=myapp_test")
|
|
if err != nil {
|
|
...
|
|
}
|
|
|
|
fixtures, err := testfixtures.New(
|
|
testfixtures.Database(db), // You database connection
|
|
testfixtures.Dialect("postgres"), // Available: "postgresql", "timescaledb", "mysql", "mariadb", "sqlite" and "sqlserver"
|
|
testfixtures.Directory("testdata/fixtures"), // the directory containing the YAML files
|
|
)
|
|
if err != nil {
|
|
...
|
|
}
|
|
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func prepareTestDatabase() {
|
|
if err := fixtures.Load(); err != nil {
|
|
...
|
|
}
|
|
}
|
|
|
|
func TestX(t *testing.T) {
|
|
prepareTestDatabase()
|
|
|
|
// Your test here ...
|
|
}
|
|
|
|
func TestY(t *testing.T) {
|
|
prepareTestDatabase()
|
|
|
|
// Your test here ...
|
|
}
|
|
|
|
func TestZ(t *testing.T) {
|
|
prepareTestDatabase()
|
|
|
|
// Your test here ...
|
|
}
|
|
```
|
|
|
|
Alternatively, you can use the `Files` option, to specify which
|
|
files you want to load into the database:
|
|
|
|
```go
|
|
fixtures, err := testfixtures.New(
|
|
testfixtures.Database(db),
|
|
testfixtures.Dialect("postgres"),
|
|
testfixtures.Files(
|
|
"fixtures/orders.yml",
|
|
"fixtures/customers.yml",
|
|
),
|
|
)
|
|
if err != nil {
|
|
...
|
|
}
|
|
|
|
fixtures, err := testfixtures.NewFiles(db, &testfixtures.PostgreSQL{},
|
|
"fixtures/orders.yml",
|
|
"fixtures/customers.yml",
|
|
// add as many files you want
|
|
)
|
|
if err != nil {
|
|
...
|
|
}
|
|
```
|
|
|
|
## Security check
|
|
|
|
In order to prevent you from accidentally wiping the wrong database, this
|
|
package will refuse to load fixtures if the database name (or database
|
|
filename for SQLite) doesn't contains "test". If you want to disable this
|
|
check, use:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.DangerousSkipTestDatabaseCheck(),
|
|
)
|
|
```
|
|
|
|
## Sequences
|
|
|
|
For PostgreSQL, this package also resets all sequences to a high
|
|
number to prevent duplicated primary keys while running the tests.
|
|
The default is 10000, but you can change that with:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.ResetSequencesTo(10000),
|
|
)
|
|
```
|
|
|
|
Or, if you want to skip the reset of sequences entirely:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.SkipResetSequences(),
|
|
)
|
|
```
|
|
|
|
## Compatible databases
|
|
|
|
### PostgreSQL / TimescaleDB
|
|
|
|
This package has two approaches to disable foreign keys while importing fixtures
|
|
for PostgreSQL databases:
|
|
|
|
#### With `DISABLE TRIGGER`
|
|
|
|
This is the default approach. For that use:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.Dialect("postgres"), // or "timescaledb"
|
|
)
|
|
```
|
|
|
|
With the above snippet this package will use `DISABLE TRIGGER` to temporarily
|
|
disabling foreign key constraints while loading fixtures. This work with any
|
|
version of PostgreSQL, but it is **required** to be connected in the database
|
|
as a SUPERUSER. You can make a PostgreSQL user a SUPERUSER with:
|
|
|
|
```sql
|
|
ALTER USER your_user SUPERUSER;
|
|
```
|
|
|
|
#### With `ALTER CONSTRAINT`
|
|
|
|
This approach don't require to be connected as a SUPERUSER, but only work with
|
|
PostgreSQL versions >= 9.4. Try this if you are getting foreign key violation
|
|
errors with the previous approach. It is as simple as using:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.Dialect("postgres"),
|
|
testfixtures.UseAlterConstraint(),
|
|
)
|
|
```
|
|
|
|
Tested using the [github.com/lib/pq](https://github.com/lib/pq) driver.
|
|
|
|
### MySQL / MariaDB
|
|
|
|
Just make sure the connection string have
|
|
[the multistatement parameter](https://github.com/go-sql-driver/mysql#multistatements)
|
|
set to true, and use:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.Dialect("mysql"), // or "mariadb"
|
|
)
|
|
```
|
|
|
|
Tested using the [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) driver.
|
|
|
|
### SQLite
|
|
|
|
SQLite is also supported. It is recommended to create foreign keys as
|
|
`DEFERRABLE` (the default) to prevent problems. See more
|
|
[on the SQLite documentation](https://www.sqlite.org/foreignkeys.html#fk_deferred).
|
|
(Foreign key constraints are no-op by default on SQLite, but enabling it is
|
|
recommended).
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.Dialect("sqlite"),
|
|
)
|
|
```
|
|
|
|
Tested using the [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) driver.
|
|
|
|
### Microsoft SQL Server
|
|
|
|
SQL Server support requires SQL Server >= 2008. Inserting on `IDENTITY` columns
|
|
are handled as well. Just make sure you are logged in with a user with
|
|
`ALTER TABLE` permission.
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.Dialect("sqlserver"),
|
|
)
|
|
```
|
|
|
|
Tested using the `mssql` and `sqlserver` drivers from the
|
|
[github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) lib.
|
|
|
|
## Templating
|
|
|
|
Testfixtures supports templating, but it's disabled by default. Most people
|
|
won't need it, but it may be useful to dynamically generate data.
|
|
|
|
Enable it by doing:
|
|
|
|
```go
|
|
testfixtures.New(
|
|
...
|
|
testfixtures.Template(),
|
|
|
|
// the above options are optional
|
|
TemplateFuncs(...),
|
|
TemplateDelims("{{", "}}"),
|
|
TemplateOptions("missingkey=zero"),
|
|
TemplateData(...),
|
|
)
|
|
```
|
|
|
|
The YAML file could look like this:
|
|
|
|
```yaml
|
|
# It's possible generate values...
|
|
- id: {{sha256 "my-awesome-post}}
|
|
title: My Awesome Post
|
|
text: {{randomText}}
|
|
|
|
# ... or records
|
|
{{range $post := $.Posts}}
|
|
- id: {{$post.Id}}
|
|
title: {{$post.Title}}
|
|
text: {{$post.Text}}
|
|
{{end}}
|
|
```
|
|
|
|
## Generating fixtures for a existing database
|
|
|
|
The following code will generate a YAML file for each table of the database
|
|
into a given folder. It may be useful to boostrap a test scenario from a sample
|
|
database of your app.
|
|
|
|
```go
|
|
dumper, err := testfixtures.NewDumper(
|
|
testfixtures.DumpDatabase(db),
|
|
testfixtures.DumpDialect("postgres"), // or your database of choice
|
|
testfixtures.DumpDirectory("tmp/fixtures"),
|
|
textfixtures.DumpTables( // optional, will dump all table if not given
|
|
"posts",
|
|
"comments",
|
|
"tags",
|
|
)
|
|
)
|
|
if err != nil {
|
|
...
|
|
}
|
|
if err := dumper.Dump(); err != nil {
|
|
...
|
|
}
|
|
```
|
|
|
|
> This was intended to run in small sample databases. It will likely break
|
|
if run in a production/big database.
|
|
|
|
## Gotchas
|
|
|
|
### Parallel testing
|
|
|
|
This library doesn't yet support running tests in parallel! Running tests
|
|
in parallel can result in random data being present in the database, which
|
|
will likely cause tests to randomly/intermittently fail.
|
|
|
|
This is specially tricky since it's not immediately clear that `go test ./...`
|
|
run tests for each package in parallel. If more than one package use this
|
|
library, you can face this issue. Please, use `go test -p 1 ./...` or run tests
|
|
for each package in separated commands to fix this issue.
|
|
|
|
If you're looking into being able to run tests in parallel you can try using
|
|
testfixtures together with the [txdb][gotxdb] package, which allows wrapping
|
|
each test run in a transaction.
|
|
|
|
## CLI
|
|
|
|
We also have a CLI to load fixtures in a given database.
|
|
Grab it from the [releases page](https://github.com/go-testfixtures/testfixtures/releases)
|
|
and use it like:
|
|
|
|
```bash
|
|
testfixtures -d postgres -c "postgres://user:password@localhost/database" -D testdata/fixtures
|
|
```
|
|
|
|
The connection string changes for each database driver.
|
|
|
|
Use `--help` for all flags.
|
|
|
|
## Contributing
|
|
|
|
We recommend you to [install Task](https://taskfile.dev/#/installation) and
|
|
Docker before contributing to this package, since some stuff is automated
|
|
using these tools.
|
|
|
|
It's recommended to use Docker Compose to run tests, since it runs tests for
|
|
all supported databases once. To do that you just need to run:
|
|
|
|
```bash
|
|
task docker
|
|
```
|
|
|
|
But if you want to run tests locally, copy the `.sample.env` file as `.env`
|
|
and edit it according to your database setup. You'll need to create a database
|
|
(likely names `testfixtures_test`) before continuing. Then run the command
|
|
for the database you want to run tests against:
|
|
|
|
```bash
|
|
task test:pg # PostgreSQL
|
|
task test:mysql # MySQL
|
|
task test:sqlite # SQLite
|
|
task test:sqlserver # Microsoft SQL Server
|
|
```
|
|
|
|
GitHub Actions (CI) runs the same Docker setup available locally.
|
|
|
|
## Alternatives
|
|
|
|
If you don't think using fixtures is a good idea, you can try one of these
|
|
packages instead:
|
|
|
|
- [factory-go][factorygo]: Factory for Go. Inspired by Python's Factory Boy
|
|
and Ruby's Factory Girl
|
|
- [go-txdb (Single transaction SQL driver for Go)][gotxdb]: Use a single
|
|
database transaction for each functional test, so you can rollback to
|
|
previous state between tests to have the same database state in all tests
|
|
- [go-sqlmock][gosqlmock]: A mock for the sql.DB interface. This allow you to
|
|
unit test database code without having to connect to a real database
|
|
- [dbcleaner][dbcleaner] - Clean database for testing, inspired by
|
|
database_cleaner for Ruby
|
|
|
|
[doc]: https://pkg.go.dev/github.com/go-testfixtures/testfixtures/v3?tab=doc
|
|
[railstests]: http://guides.rubyonrails.org/testing.html#the-test-database
|
|
[gotxdb]: https://github.com/DATA-DOG/go-txdb
|
|
[gosqlmock]: https://github.com/DATA-DOG/go-sqlmock
|
|
[factorygo]: https://github.com/bluele/factory-go
|
|
[dbcleaner]: https://github.com/khaiql/dbcleaner
|