To hack on pggen
you will need a postgres database to test against.
Make sure you have postgres installed, then from the root of the repo run
> createdb pggen_development
> psql pggen_development < cmd/pggen/test/db.sql
You will need to re-run the command loading cmd/pggen/test/db.sql
any time
you make a change to the test data or schema defined in that file. Once
the test database is set up you can run all tests by doing
> go generate ./...
> go test ./...
Most of the tests are defined in the cmd/pggen/test
package, so if you
want to focus on a specific test, you will probably want to look there.
If you want, you can use almost the same docker set up that pggen
uses for
continuous integration for local development. The setup for local development
differs in that it uses a docker volume so that changes on your local file system
are picked up and tested without needing to re-build the image each time.
First, build the development docker image:
> docker-compose build
once you have the image built, you can either run the tests start to finish with
> docker-compose run test11
or debug interactively by opening a bash shell with
> docker-compose run test11 bash
The full test suite runs against multiple versions of postgres. The above examples
just work with postgres 11, but you can see the other versions of postgres that
pggen
is tested against by reading docker-compose.yml
.
pggen/test/cli_test.go
contains some end-to-end tests of the pggen
command
line utility. These tests spawn subprocesses and then perform some assertions
about the stdout, stderr, and return code of the subprocesses. If one of them
fails, it will print the command used to execute the failing test, but you won't
be able to re-run the command because the test will have cleaned up after
itself. You can enable re-running the cli tests with
> cd cmd/pggen/test
> PGGEN_DEBUG_CLI=1 go test --run TestCLI
This will both prevent the cli tests from removing their scratch dir and compile the binary under test in a debugger friendly format so that you can immediately point your delve at it.
A great way to both document pggen and provide more end-to-end test coverage is to add a
new example to the examples
directory. Examples must follow a very specific format in order
to be run by the test suite. This format is specified in the module comment at the top of
examples/examples_test.go
. If you are working on developing just a specific example, it is
worth knowing about the PGGEN_TEST_EXAMPLE
environment variable, which can be used to focus
on one or more examples rather than running the whole suite every time.
When modifying the output of the codegenerator, you are likely to introduce compile
errors in the generated code. Because we run the go/format
package over our output
before landing it to a disk you won't be able to debug the issue by looking at the
generated file by default. In order to make this easier, you can set PGGEN_GOFMT=off
in the environment. This will prevent pggen from formatting the generated code
and make it easier to debug the output from pggen.
pggen
runs a number of different CI checks in some docker containers orchestrated by
.circleci/docker-compose.yml
. Using docker adds some inefficiency and slows down the
CI checks a bit, but hopefully this is made up for by the fact that the docker-compose.yml
file in the repo root makes debugging CI jobs easier. We maintain two different compose files
so that we can share the source between the host and the container during local development.
pggen
supports using both github.com/lib/pq
and github.com/jackc/pgx/v4/stdlib
as
database drivers. jackc/pgx
is recommended because lib/pq
is unmaintained.
The example tests all use the recommended driver (jackc/pgx
) for testing to keep the example code
simple. The main test suite cmd/pggen/test
is parameterized over the driver though. It
allows you to you set the driver name via the DB_DRIVER
environment variable. You can
either set this variable to postgres
(to use lib/pq
) or pgx
(to use
github.com/jackc/pgx/v4/stdlib
).
The tools/test.bash
script runs the test suite both ways.
Certain aspects of the style in which pggen
is developed are intentionally divergent
from common practices in other places. A few of these are called out here with some
explanation as to why things are the way they are.
pggen
uses the built in go unit test framework. This is good because tests are code, not English,
and trying to make them look like English tens to encourage tests that fail to explore the state
space programatically. pggen
will never use a test framework like ginkgo. A flexible assertions
framework like gomega may be considered, but it is likely better to leverage libraries like
google/go-cmp
and pattern matching DSLs like regular expressions. Using a randomized testing
framework in the spirit of quickcheck or a fuzz tester would definitely be considered.
Pggen has no external components that cannot be easily controlled, so pggen
tests do not
and will never use mocks. Mocking should be a last resort when testing with the real thing is
impractical. This avoidance of mocks has the great side benefit of preventing the codebase
from sprouting superfluous interfaces.