pgxmock: testing pgx code without a PostgreSQL connection
pgx mock driver for golang to test database interactions
At a glance
- What is it?
- pgxmock is a mock driver for the pgx PostgreSQL toolkit in Go, built on the design of sqlmock, so tests can assert on queries, arguments, results and transaction behaviour without a live database. It is strict by default and it deliberately stops short of validating SQL semantics.
- Who is it for?
- pgxmock fits Go projects that use pgx directly and want fast, hermetic unit tests that assert on query text, arguments and transaction outcomes, especially when the production code takes a narrow interface. It does not fit a team that needs its SQL semantics checked, because a mock never parses or executes a statement.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 18 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What pgxmock is and the problem it removes
pgxmock is a mock library that implements pgx, the PostgreSQL driver and toolkit for Go. It is based on the well-known sqlmock library, which does the same job for the standard database/sql driver interface, and it inherits that design. The README states the purpose in one line, to simulate pgx behaviour in tests without needing a real database connection, with the stated purpose of maintaining a correct test-driven development workflow.
The problem it removes is concrete. In a Go service that talks to PostgreSQL through pgx, the usual unit test needs either a database or a fake that implements the same interface, and the first option means provisioning, migrations and a cleanup story in every test run. pgxmock gives you a fake that speaks the driver's contract, records what was asked of it and answers with results you declare in advance.
The README lists three properties that matter when choosing a test double. It does not require any modifications to your source code, which is what makes it usable against an interface you already have rather than one you refactor for testability. It has strict by default expectation order matching, so a query in the wrong sequence fails the test instead of quietly matching. And it has no third-party dependencies except the pgx packages, with testify used only by the project's own tests, so the library does not drag a dependency tree into your module.
The module is versioned as v6, matching the major version of pgx it targets, and the go.mod requires pgx v5.11.0 with Go 1.25.0 as the language floor. The repository layout shows how mature the surface is: the implementation is split across driver.go, expectations.go, query.go, rows.go, batch.go, result.go, argument.go, modifiers.go, options.go and errors.go, and the root directory carries a long list of focused test files such as concurrency_test.go, notification_test.go, null_scan_test.go, copyfrom_rows_test.go and target_session_attrs_test.go.
The expectation model: ordered calls, results and transaction outcomes
The usage pattern in the README is the classic sqlmock cycle: create a pool mock, declare expectations, run the code under test, then assert that every expectation was met.
mock, err := pgxmock.NewPool()
if err != nil {
t.Fatal(err)
}
defer mock.Close()
mock.ExpectBegin()
mock.ExpectExec("UPDATE products").
WillReturnResult(pgxmock.NewResult("UPDATE", 1))
mock.ExpectExec("INSERT INTO product_viewers").
WithArgs(2, 3).
WillReturnResult(pgxmock.NewResult("INSERT", 1))
mock.ExpectCommit()Each expectation names a call and its answer. ExpectBegin and ExpectCommit bracket a transaction, ExpectExec matches a statement with a regular expression and returns a result built with NewResult, and WithArgs pins the arguments. The code under test receives the mock in place of the pool, and ExpectationsWereMet at the end is what turns a silent mismatch into a failure.
The failing case in the README shows the other half of the model. When the second statement fails, the expectation returns an error instead of a result, and the transaction is expected to roll back with ExpectRollback rather than commit. That pairing is what lets a test assert on transaction semantics, not just on the queries that were issued.
The examples directory holds four complete programs that show the same API applied to different shapes of work: the simplest one, a blog API server, a copyfrom example and a batch example. The copyfrom and batch entries matter because they cover the parts of the pgx surface that plain Exec and Query do not reach, and the repository has matching test files at the root for copyfrom_rows, batch and row mapping.
Installing pgxmock and the Go version support policy
The library is installed as a normal Go module dependency.
go get github.com/pashagolub/pgxmock/v6The version support policy is stated explicitly and follows the official Go release policy: the two most recent major Go releases are supported, and older versions may work but are not tested or maintained. In practice that means a module on an old Go line will still compile against pgxmock, but a bug found on that line will not be chased, and a new pgx release may be adopted without waiting for old Go users to catch up.
For a project already using pgx v5, the dependency footprint is a single line in go.mod. The indirect requirements that pgx itself pulls in, pgpassfile, pgservicefile, puddle/v2, go.yaml.in/yaml/v3, and the golang.org/x/sync and golang.org/x/text modules, come from pgx rather than from the mock, and the project's own tests add testify. That is a small surface for a library that sits in your test build only, and it is the reason pgxmock can keep its promise of no third-party dependencies beyond pgx.
Query matchers: regexp by default, then equal, substring and any
SQL string matching is where most mock libraries get argued about, and pgxmock makes it a first-class decision. The QueryMatcher interface can be passed through an option when creating a mock with NewPool or NewConn.
mock, err := pgxmock.NewPool(pgxmock.QueryMatcherOption(pgxmock.QueryMatcherEqual))The default is QueryMatcherRegexp, which uses the expected SQL string as a regular expression to match the incoming query, and the README is explicit that this choice preserves backward compatibility. Three alternatives cover the common cases. QueryMatcherEqual does a full case-sensitive match, ignoring differences in whitespace. QueryMatcherSubstring checks that the query contains the expected string, without the escaping a regular expression needs, which is the practical choice when the query text is long and you only care about one clause. QueryMatcherAny disables SQL matching altogether, so any statement satisfies the expectation.
The reason to implement the interface yourself is that it lets a library parse and validate a SQL AST and match on structure rather than on text. The README draws the line explicitly: pgxmock will not provide standard SQL parsing matchers, so if you want to assert that a query parses or uses a particular join form, you supply the matcher. That is a sensible division of labour, and it also means the strictness of your assertions is your choice rather than the library's.
Loading custom types and reporting the connection configuration
PostgreSQL types complicate mocking, because a driver loads enums, composites, domains and ranges at startup and registers them, and the code under test then depends on that registration happening. pgxmock mocks the loading step while leaving the registration with the code under test.
// the code under test
type typeLoader interface {
LoadTypes(ctx context.Context, typeNames []string) ([]*pgtype.Type, error)
TypeMap() *pgtype.Map
}
func registerTypes(ctx context.Context, conn typeLoader) error {
types, err := conn.LoadTypes(ctx, []string{"status"})
if err != nil {
return err
}
conn.TypeMap().RegisterTypes(types)
return nil
}ExpectLoadTypes, with the singular ExpectLoadType as a variant, answers the load with the types you declare, and the test then registers them through the connection's type map exactly as production code would. In the README example the loaded type is an enum named status with an OID and an EnumCodec, and the assertion checks that the type is present in the map afterwards. This mirrors pgx behaviour rather than shortcutting it, which means the test still exercises the registration path you care about.
The same philosophy shows up in the connection configuration. By default the mock's Config method reports a zero configuration, so code that reads its own settings back off the pool sees an empty struct unless you hand the mock a configuration to report. It is a small detail, but it is the kind of detail that decides whether a mock can stand in for a pool in a test that inspects connection settings.
Where pgxmock stops and what to use instead
The boundary of this approach is SQL semantics. A mock matches a string and returns a canned result; it does not parse the statement, does not execute it, and does not know whether your query would return three rows or none. A test that asserts the right query text was sent is a different guarantee from a test that asserts the query returns what the code assumes, and pgxmock provides the first.
The realistic alternative is a real PostgreSQL in a container, started per test run, with migrations applied. The difference in approach is the difference between a fake at the driver boundary and the actual server: the container exercises the planner, the type system, the constraint checks and the transaction semantics, so the assertions are about behaviour rather than about requests, at the cost of startup time, image management and a flaky-test risk that grows with the number of concurrent suites.
The middle path is to use both, and it is what the pgxmock README's own framing supports. A mock is at its best where the logic under test is branching on the database result, an error path, or a transaction decision, which is exactly where a fast unit test earns its keep. Anything that depends on the database's own behaviour, a constraint, a view or a stored function, belongs in a test against a real server. The third option, sqlmock, is the closer cousin for code written against database/sql rather than pgx, and it follows the same expectation model, so teams migrating between the two drivers find the shape familiar.
Editorial conclusion
pgxmock fits Go projects that use pgx directly and want fast, hermetic unit tests that assert on query text, arguments and transaction outcomes, especially when the production code takes a narrow interface. It does not fit a team that needs its SQL semantics checked, because a mock never parses or executes a statement. Verify first that your Go version is one of the two most recent major releases the project supports, that the interface under test is small enough to mock, and that the query matcher you choose matches how strictly your assertions should behave.
Frequently asked questions
What is pgxmock and what does it replace in tests?
It is a mock implementation of the pgx PostgreSQL driver and toolkit for Go, based on the sqlmock library. It simulates pgx behaviour in tests so no real database connection is needed, and it does not require changes to the code under test.
How do I install pgxmock?
Run `go get github.com/pashagolub/pgxmock/v6`. The module is v6 and its go.mod requires pgx v5.11.0 with Go 1.25.0 as the language floor.
How does pgxmock match SQL queries?
Through the QueryMatcher interface, passed as an option to NewPool or NewConn. The default is QueryMatcherRegexp, and the alternatives are QueryMatcherEqual, QueryMatcherSubstring and QueryMatcherAny, which disables matching altogether.
How do I test code that loads custom PostgreSQL types?
Use ExpectLoadTypes or ExpectLoadType to return the types the driver would load, and let the code under test register them in the type map as it does in production. The README example loads an enum named status and then asserts it is present in the type map.
Which Go versions does pgxmock support?
It follows the official Go release policy, supporting the two most recent major Go releases. Older versions may work but are not tested or maintained.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pashagolub-pgxmock)Was this useful?