Postgresr (pronounced post-gres-er) is a thin wrapper round the library pgx. It provides interfaces and mocks that can be used for establishing real connections, as well as mocking your database interactions for unit testing. As of this writing this does not implement all methods on pgx.Conn, but it implements enough for my purposes.
go get github.com/app-nerds/postgrer
package main
import (
"context"
"github.com/app-nerds/postgresr"
)
func main() {
var (
err error
db postgresr.Conn
)
if db, err = postgresr.Connect(context.Background(), "host=localhost dbname=example user=user password=password"); err != nil {
panic("cannot connect to database!")
}
}
This library provides mock structures useful for unit tests. Here is an example of a test that mocks a Postgres variable passed to a function.
func QueryForStuff(pg postgresr.Conn) ([]SomeStruct, error) {
var (
err error
rows pgx.Rows
result []SomeStruct
)
query := `SELECT * FROM that_table WHERE something='else'`
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
if rows, err = pg.Query(ctx, query); err != nil {
// handle it
}
for rows.Next() {
var (
column1 string
column2 int
)
if err = rows.Scan(&column1, &column2); err != nil {
// handle it
}
result = append(result, SomeStruct{
Column1: column1,
Column2: column2,
})
}
return result, nil
}
func TestQueryForStuff(t *testing.T) {
testData := [][]interface{}{
{
"value1", // column1
1, // column2
},
{
"value2",
2,
},
}
pg := &postgresr.MockConn{
QueryFunc: postgresr.MockQuerySuccessHelper(testData),
}
want := []SomeStruct{
{ Column1: "value1", Column2: 1 },
{ Column1: "value2", Column2: 2 },
}
got, err := QueryForStuff(pg)
if err != nil {
t.Errorf("didn't expect an error!")
}
if !reflect.DeepEqual(want, got) {
t.Errorf("wanted: %+v\ngot: %+v\n", want, got)
}
}
If you need more control of what you return in query mocks, the DataToRows
method might be useful.
func TestQueryForStuff(t *testing.T) {
var (
data1CurrentRow *int
data1TotalCount *int
data2CurrentRow *int
data2TotalCount *int
)
testData1 := [][]interface{}{
{
"value1", // column1
1, // column2
},
{
"value2",
2,
},
}
testData2 := [][]interface{}{
{ 1, "1" },
{ 2, "2" },
}
data1Counter := postgresr.InitializeRowCounterFunc(data1CurrentRow, data1TotalCount, len(testData1))
data2Counter := postgresr.InitializeRowCounterFunc(data2CurrentRow, data2TotalCount, len(testData2))
pg := &postgresr.MockConn{
QueryFunc: func(ctx context.Context, query string, arguments ...interface{}) (pgx.Rows, error) {
if strings.Contains(query, "FROM table1") {
return postgresr.DataToRows(testData1, data1Counter), nil
}
return postgresr.DataToRows(testData2, data2Counter), nil
},
}
want := []SomeStruct{
{ Column1: "value1", Column2: 1 },
{ Column1: "value2", Column2: 2 },
}
got, err := QueryForStuff(pg)
if err != nil {
t.Errorf("didn't expect an error!")
}
if !reflect.DeepEqual(want, got) {
t.Errorf("wanted: %+v\ngot: %+v\n", want, got)
}
}