Browse Source
upgrade to use testfixtures v3 (#11904)
upgrade to use testfixtures v3 (#11904)
* upgrade to use testfixtures v3 * simplify logic * make vendor * update per @lunny * Update templates/repo/empty.tmpl * Update templates/repo/empty.tmpl Co-authored-by: Lauris BH <lauris@nix.lv>mj-v1.14.3
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 8813 additions and 5463 deletions
-
3contrib/pr/checkout.go
-
8go.mod
-
15go.sum
-
16integrations/integration_test.go
-
40models/test_fixtures.go
-
3models/unit_tests.go
-
0vendor/github.com/go-testfixtures/testfixtures/v3/.editorconfig
-
0vendor/github.com/go-testfixtures/testfixtures/v3/.gitattributes
-
4vendor/github.com/go-testfixtures/testfixtures/v3/.gitignore
-
41vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml
-
1vendor/github.com/go-testfixtures/testfixtures/v3/.sample.env
-
93vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md
-
9vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile
-
0vendor/github.com/go-testfixtures/testfixtures/v3/LICENSE
-
483vendor/github.com/go-testfixtures/testfixtures/v3/README.md
-
59vendor/github.com/go-testfixtures/testfixtures/v3/Taskfile.yml
-
37vendor/github.com/go-testfixtures/testfixtures/v3/docker-compose.yml
-
165vendor/github.com/go-testfixtures/testfixtures/v3/dump.go
-
14vendor/github.com/go-testfixtures/testfixtures/v3/go.mod
-
26vendor/github.com/go-testfixtures/testfixtures/v3/go.sum
-
14vendor/github.com/go-testfixtures/testfixtures/v3/helper.go
-
0vendor/github.com/go-testfixtures/testfixtures/v3/json.go
-
35vendor/github.com/go-testfixtures/testfixtures/v3/mysql.go
-
63vendor/github.com/go-testfixtures/testfixtures/v3/postgresql.go
-
11vendor/github.com/go-testfixtures/testfixtures/v3/sqlite.go
-
64vendor/github.com/go-testfixtures/testfixtures/v3/sqlserver.go
-
599vendor/github.com/go-testfixtures/testfixtures/v3/testfixtures.go
-
43vendor/github.com/go-testfixtures/testfixtures/v3/time.go
-
6vendor/github.com/mattn/go-sqlite3/.travis.yml
-
57vendor/github.com/mattn/go-sqlite3/README.md
-
8vendor/github.com/mattn/go-sqlite3/backup.go
-
26vendor/github.com/mattn/go-sqlite3/callback.go
-
299vendor/github.com/mattn/go-sqlite3/convert.go
-
21vendor/github.com/mattn/go-sqlite3/error.go
-
9191vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c
-
81vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h
-
154vendor/github.com/mattn/go-sqlite3/sqlite3.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_context.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_go18.go
-
3vendor/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension_omit.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_allow_uri_authority.go
-
4vendor/github.com/mattn/go-sqlite3/sqlite3_opt_app_armor.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_foreign_keys.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_fts5.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_icu.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_introspect.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_json1.go
-
20vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate.go
-
112vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go
-
21vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_omit.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_secure_delete.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_secure_delete_fast.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_stat4.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vacuum_full.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vacuum_incr.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_other.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_solaris.go
-
3vendor/github.com/mattn/go-sqlite3/sqlite3_trace.go
-
5vendor/github.com/mattn/go-sqlite3/sqlite3_type.go
-
2vendor/github.com/mattn/go-sqlite3/sqlite3_windows.go
-
4vendor/github.com/mattn/go-sqlite3/sqlite3ext.h
-
5vendor/github.com/mattn/go-sqlite3/static_mock.go
-
7vendor/github.com/spf13/pflag/.travis.yml
-
4vendor/github.com/spf13/pflag/README.md
-
38vendor/github.com/spf13/pflag/bool_slice.go
-
4vendor/github.com/spf13/pflag/count.go
-
38vendor/github.com/spf13/pflag/duration_slice.go
-
16vendor/github.com/spf13/pflag/flag.go
-
174vendor/github.com/spf13/pflag/float32_slice.go
-
166vendor/github.com/spf13/pflag/float64_slice.go
-
3vendor/github.com/spf13/pflag/go.mod
-
0vendor/github.com/spf13/pflag/go.sum
-
174vendor/github.com/spf13/pflag/int32_slice.go
-
166vendor/github.com/spf13/pflag/int64_slice.go
-
30vendor/github.com/spf13/pflag/int_slice.go
-
40vendor/github.com/spf13/pflag/ip_slice.go
-
26vendor/github.com/spf13/pflag/string_array.go
-
22vendor/github.com/spf13/pflag/string_slice.go
-
149vendor/github.com/spf13/pflag/string_to_int64.go
-
42vendor/github.com/spf13/pflag/uint_slice.go
-
26vendor/gopkg.in/testfixtures.v2/.travis.yml
-
358vendor/gopkg.in/testfixtures.v2/README.md
-
64vendor/gopkg.in/testfixtures.v2/Taskfile.yml
-
51vendor/gopkg.in/testfixtures.v2/appveyor.yml
-
75vendor/gopkg.in/testfixtures.v2/deprecated.go
-
41vendor/gopkg.in/testfixtures.v2/errors.go
-
110vendor/gopkg.in/testfixtures.v2/generate.go
-
19vendor/gopkg.in/testfixtures.v2/options.go
-
171vendor/gopkg.in/testfixtures.v2/oracle.go
-
305vendor/gopkg.in/testfixtures.v2/testfixtures.go
-
34vendor/gopkg.in/testfixtures.v2/time.go
-
1vendor/gopkg.in/yaml.v2/apic.go
-
16vendor/modules.txt
@ -0,0 +1,41 @@ |
|||
build: |
|||
binary: testfixtures |
|||
main: ./cmd/testfixtures |
|||
goos: |
|||
- windows |
|||
- darwin |
|||
- linux |
|||
goarch: |
|||
- 386 |
|||
- amd64 |
|||
ignore: |
|||
- goos: darwin |
|||
goarch: 386 |
|||
flags: |
|||
- -tags=sqlite |
|||
|
|||
archives: |
|||
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}" |
|||
format_overrides: |
|||
- goos: windows |
|||
format: zip |
|||
|
|||
release: |
|||
draft: true |
|||
|
|||
snapshot: |
|||
name_template: "{{.Tag}}" |
|||
|
|||
checksum: |
|||
name_template: "testfixtures_checksums.txt" |
|||
|
|||
nfpms: |
|||
- vendor: testfixtures |
|||
homepage: https://github.com/go-testfixtures/testfixtures |
|||
maintainer: Andrey Nering <andrey.nering@gmail.com> |
|||
description: Ruby on Rails like test fixtures for Go. |
|||
license: MIT |
|||
formats: |
|||
- deb |
|||
- rpm |
|||
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}" |
@ -0,0 +1,93 @@ |
|||
# Changelog |
|||
|
|||
## v3.2.0 - 2020-05-10 |
|||
|
|||
- Add support for loading multiple files and directories |
|||
([#65](https://github.com/go-testfixtures/testfixtures/pull/65)). |
|||
|
|||
## v3.1.2 - 2020-04-26 |
|||
|
|||
- Dump: Fix column order in generated YAML files |
|||
([#62](https://github.com/go-testfixtures/testfixtures/pull/62)). |
|||
|
|||
## v3.1.1 - 2020-01-11 |
|||
|
|||
- testfixtures now work with both `mssql` and `sqlserver` drivers. |
|||
Note that [the `mssql` one is deprecated](https://github.com/denisenkom/go-mssqldb#deprecated), |
|||
though. So try to migrate to `sqlserver` once possible. |
|||
|
|||
## v3.1.0 - 2020-01-09 |
|||
|
|||
- Using `sqlserver` driver instead of the deprecated `mssql` |
|||
([#58](https://github.com/go-testfixtures/testfixtures/pull/58)). |
|||
|
|||
## v3.0.0 - 2019-12-26 |
|||
|
|||
### Breaking changes |
|||
|
|||
- The import path changed from `gopkg.in/testfixtures.v2` to |
|||
`github.com/go-testfixtures/testfixtures/v3`. |
|||
- This package no longer support Oracle databases. This decision was |
|||
taken because too few people actually used this package with Oracle and it |
|||
was the most difficult to test (we didn't run on CI due the lack of an |
|||
official Docker image, etc). |
|||
- The public API was totally rewritten to be more flexible and ideomatic. |
|||
It now uses functional options. It differs from v2, but should be easy |
|||
enough to upgrade. |
|||
- Some deprecated APIs from v2 were removed as well. |
|||
- This now requires Go >= 1.13. |
|||
|
|||
### New features |
|||
|
|||
- We now have a CLI so you can easily use testfixtures to load a sample |
|||
database from fixtures if you want. |
|||
- Templating via [text/template](https://golang.org/pkg/text/template/) |
|||
is now available. This allows some fancier use cases like generating data |
|||
or specific columns dynamically. |
|||
- It's now possible to choose which time zone to use when parsing timestamps |
|||
from fixtures. The default is the same as before, whatever is set on |
|||
`time.Local`. |
|||
- Errors now use the new `%w` verb only available on Go >= 1.13. |
|||
|
|||
### MISC |
|||
|
|||
- Travis and AppVeyor are gone. We're using GitHub Actions exclusively now. |
|||
The whole suite is ran inside Docker (with help of Docker Compose), so it's |
|||
easy to run tests locally as well. |
|||
|
|||
Check the new README for some examples! |
|||
|
|||
## v2.6.0 - 2019-10-24 |
|||
|
|||
- Add support for TimescaleDB |
|||
([#53](https://github.com/go-testfixtures/testfixtures/pull/53)). |
|||
|
|||
## v2.5.3 - 2018-12-15 |
|||
|
|||
- Fixes related to use of foreign key pragmas on MySQL (#43). |
|||
|
|||
## v2.5.2 - 2018-11-25 |
|||
|
|||
- This library now supports [Go Modules](https://github.com/golang/go/wiki/Modules); |
|||
- Also allow `.yaml` (as an alternative to `.yml`) as the file extension (#42). |
|||
|
|||
## v2.5.1 - 2018-11-04 |
|||
|
|||
- Allowing disabling reset of PostgreSQL sequences (#38). |
|||
|
|||
## v2.5.0 - 2018-09-07 |
|||
|
|||
- Add public function DetectTestDatabase (#35, #36). |
|||
|
|||
## v2.4.5 - 2018-07-07 |
|||
|
|||
- Fix for MySQL/MariaDB: ignoring views on operations that should be run only on tables (#33). |
|||
|
|||
## v2.4.4 - 2018-07-02 |
|||
|
|||
- Fix for multiple schemas on Microsoft SQL Server (#29 and #30); |
|||
- Configuring AppVeyor CI to also test for Microsoft SQL Server. |
|||
|
|||
--- |
|||
|
|||
Sorry, we don't have changelog for older releases 😢. |
@ -0,0 +1,9 @@ |
|||
FROM golang:1.14-alpine |
|||
|
|||
RUN apk update |
|||
RUN apk add alpine-sdk |
|||
|
|||
WORKDIR /testfixtures |
|||
COPY . . |
|||
|
|||
RUN go mod download |
@ -0,0 +1,483 @@ |
|||
# testfixtures |
|||
|
|||
[][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 { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
With `Paths` option, you can specify the paths that fixtures will load |
|||
from. Path can be directory or file. If directory, we will search YAML files |
|||
in it. |
|||
|
|||
```go |
|||
fixtures, err := testfixtures.New( |
|||
testfixtures.Database(db), |
|||
testfixtures.Dialect("postgres"), |
|||
testfixtures.Paths( |
|||
"fixtures/orders.yml", |
|||
"fixtures/customers.yml", |
|||
"common_fixtures/users" |
|||
), |
|||
) |
|||
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 |
@ -0,0 +1,59 @@ |
|||
# https://taskfile.org |
|||
|
|||
version: '2' |
|||
|
|||
tasks: |
|||
build: |
|||
cmds: |
|||
- go build -v -tags sqlite -o ./testfixtures{{exeExt}} ./cmd/testfixtures |
|||
|
|||
test-cli: |
|||
cmds: |
|||
- ./testfixtures -d sqlite -c testdb.sqlite3 -D testdata/fixtures |
|||
|
|||
test:pg: |
|||
desc: Test PostgreSQL |
|||
cmds: |
|||
- task: test-db |
|||
vars: {DATABASE: postgresql} |
|||
|
|||
test:mysql: |
|||
desc: Test MySQL |
|||
cmds: |
|||
- task: test:db |
|||
vars: {DATABASE: mysql} |
|||
|
|||
test:sqlite: |
|||
desc: Test SQLite |
|||
cmds: |
|||
- task: test-db |
|||
vars: {DATABASE: sqlite} |
|||
|
|||
test:sqlserver: |
|||
desc: Test SQLServer |
|||
cmds: |
|||
- task: test-db |
|||
vars: {DATABASE: sqlserver} |
|||
|
|||
test-db: |
|||
cmds: |
|||
- go test -v -tags {{.DATABASE}} |
|||
|
|||
goreleaser:test: |
|||
desc: Tests release process without publishing |
|||
cmds: |
|||
- goreleaser --snapshot --rm-dist |
|||
|
|||
docker: |
|||
cmds: |
|||
- task: docker:build |
|||
- task: docker:test |
|||
|
|||
docker:build: |
|||
cmds: |
|||
- docker build -t testfixtures . |
|||
|
|||
docker:test: |
|||
cmds: |
|||
- docker-compose down -v |
|||
- docker-compose run testfixtures go test -v -tags 'postgresql sqlite mysql sqlserver' |
@ -0,0 +1,37 @@ |
|||
version: '3' |
|||
|
|||
services: |
|||
testfixtures: |
|||
image: testfixtures |
|||
depends_on: |
|||
- postgresql |
|||
- mysql |
|||
- sqlserver |
|||
environment: |
|||
PGPASSWORD: postgres |
|||
PG_CONN_STRING: host=postgresql user=postgres dbname=testfixtures_test port=5432 sslmode=disable |
|||
|
|||
MYSQL_CONN_STRING: root:mysql@tcp(mysql)/testfixtures_test?multiStatements=true |
|||
|
|||
SQLITE_CONN_STRING: testfixtures_test.sqlite3 |
|||
|
|||
SQLSERVER_CONN_STRING: server=sqlserver;database=master;user id=sa;password=SQL@1server;encrypt=disable |
|||
|
|||
postgresql: |
|||
image: postgres:12.1-alpine |
|||
environment: |
|||
POSTGRES_DB: testfixtures_test |
|||
POSTGRES_USER: postgres |
|||
POSTGRES_PASSWORD: postgres |
|||
|
|||
mysql: |
|||
image: mysql:8.0 |
|||
environment: |
|||
MYSQL_DATABASE: testfixtures_test |
|||
MYSQL_ROOT_PASSWORD: mysql |
|||
|
|||
sqlserver: |
|||
image: mcr.microsoft.com/mssql/server:2019-latest |
|||
environment: |
|||
ACCEPT_EULA: 'Y' |
|||
SA_PASSWORD: SQL@1server |
@ -0,0 +1,165 @@ |
|||
package testfixtures |
|||
|
|||
import ( |
|||
"database/sql" |
|||
"fmt" |
|||
"os" |
|||
"path/filepath" |
|||
"unicode/utf8" |
|||
|
|||
"gopkg.in/yaml.v2" |
|||
) |
|||
|
|||
// Dumper is resposible for dumping fixtures from the database into a
|
|||
// directory.
|
|||
type Dumper struct { |
|||
db *sql.DB |
|||
helper helper |
|||
dir string |
|||
|
|||
tables []string |
|||
} |
|||
|
|||
// NewDumper creates a new dumper with the given options.
|
|||
//
|
|||
// The "DumpDatabase", "DumpDialect" and "DumpDirectory" options are required.
|
|||
func NewDumper(options ...func(*Dumper) error) (*Dumper, error) { |
|||
d := &Dumper{} |
|||
|
|||
for _, option := range options { |
|||
if err := option(d); err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
return d, nil |
|||
} |
|||
|
|||
// DumpDatabase sets the database to be dumped.
|
|||
func DumpDatabase(db *sql.DB) func(*Dumper) error { |
|||
return func(d *Dumper) error { |
|||
d.db = db |
|||
return nil |
|||
} |
|||
} |
|||
|
|||
// DumpDialect informs Loader about which database dialect you're using.
|
|||
//
|
|||
// Possible options are "postgresql", "timescaledb", "mysql", "mariadb",
|
|||
// "sqlite" and "sqlserver".
|
|||
func DumpDialect(dialect string) func(*Dumper) error { |
|||
return func(d *Dumper) error { |
|||
h, err := helperForDialect(dialect) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
d.helper = h |
|||
return nil |
|||
} |
|||
} |
|||
|
|||
// DumpDirectory sets the directory where the fixtures files will be created.
|
|||
func DumpDirectory(dir string) func(*Dumper) error { |
|||
return func(d *Dumper) error { |
|||
d.dir = dir |
|||
return nil |
|||
} |
|||
} |
|||
|
|||
// DumpTables allows you to choose which tables you want to dump.
|
|||
//
|
|||
// If not informed, Dumper will dump all tables by default.
|
|||
func DumpTables(tables ...string) func(*Dumper) error { |
|||
return func(d *Dumper) error { |
|||
d.tables = tables |
|||
return nil |
|||
} |
|||
} |
|||
|
|||
// Dump dumps the databases as YAML fixtures.
|
|||
func (d *Dumper) Dump() error { |
|||
tables := d.tables |
|||
if len(tables) == 0 { |
|||
var err error |
|||
tables, err = d.helper.tableNames(d.db) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
|
|||
for _, table := range tables { |
|||
if err := d.dumpTable(table); err != nil { |
|||
return err |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (d *Dumper) dumpTable(table string) error { |
|||
query := fmt.Sprintf("SELECT * FROM %s", d.helper.quoteKeyword(table)) |
|||
|
|||
stmt, err := d.db.Prepare(query) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer stmt.Close() |
|||
|
|||
rows, err := stmt.Query() |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer rows.Close() |
|||
|
|||
columns, err := rows.Columns() |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
fixtures := make([]yaml.MapSlice, 0, 10) |
|||
for rows.Next() { |
|||
entries := make([]interface{}, len(columns)) |
|||
entryPtrs := make([]interface{}, len(entries)) |
|||
for i := range entries { |
|||
entryPtrs[i] = &entries[i] |
|||
} |
|||
if err := rows.Scan(entryPtrs...); err != nil { |
|||
return err |
|||
} |
|||
|
|||
entryMap := make([]yaml.MapItem, len(entries)) |
|||
for i, column := range columns { |
|||
entryMap[i] = yaml.MapItem{ |
|||
Key: column, |
|||
Value: convertValue(entries[i]), |
|||
} |
|||
} |
|||
fixtures = append(fixtures, entryMap) |
|||
} |
|||
if err = rows.Err(); err != nil { |
|||
return err |
|||
} |
|||
|
|||
filePath := filepath.Join(d.dir, table+".yml") |
|||
f, err := os.Create(filePath) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer f.Close() |
|||
|
|||
data, err := yaml.Marshal(fixtures) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, err = f.Write(data) |
|||
return err |
|||
} |
|||
|
|||
func convertValue(value interface{}) interface{} { |
|||
switch v := value.(type) { |
|||
case []byte: |
|||
if utf8.Valid(v) { |
|||
return string(v) |
|||
} |
|||
} |
|||
return value |
|||
} |
@ -0,0 +1,14 @@ |
|||
module github.com/go-testfixtures/testfixtures/v3 |
|||
|
|||
require ( |
|||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 |
|||
github.com/go-sql-driver/mysql v1.4.1 |
|||
github.com/joho/godotenv v1.3.0 |
|||
github.com/lib/pq v1.3.0 |
|||
github.com/mattn/go-sqlite3 v2.0.2+incompatible |
|||
github.com/spf13/pflag v1.0.5 |
|||
google.golang.org/appengine v1.3.0 // indirect |
|||
gopkg.in/yaml.v2 v2.2.7 |
|||
) |
|||
|
|||
go 1.13 |
@ -0,0 +1,26 @@ |
|||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA= |
|||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= |
|||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= |
|||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= |
|||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= |
|||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= |
|||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
|||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= |
|||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= |
|||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= |
|||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= |
|||
github.com/mattn/go-sqlite3 v2.0.2+incompatible h1:qzw9c2GNT8UFrgWNDhCTqRqYUSmu/Dav/9Z58LGpk7U= |
|||
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= |
|||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= |
|||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= |
|||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= |
|||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
|||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
|||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
|||
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= |
|||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= |
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= |
|||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |