apitest
Reports

Database interactions

Show the SQL queries a handler makes in its sequence diagram, with the x/db package.

The x/db package wraps any database/sql driver so that the queries made during a test are recorded and drawn in the sequence diagram.

An apitest report for GET /todos/1, with the SQL query sent to sqlite and its result in the diagram and the list of messages
Open the live report

Setup

import apitestdb "github.com/steinfletcher/apitest/x/db"

Wrap and register the driver

Create a recorder, wrap the driver you already use, and register the wrapped driver under a new name. This example uses the pure Go modernc.org/sqlite driver, which registers itself as sqlite.

var recorder = apitest.NewTestRecorder()

func init() {
	// wrap the driver, then register the wrapped version under a new name
	sql.Register("sqlite-recorded", apitestdb.WrapWithRecorder("sqlite", recorder))
}

Open the database through the wrapped driver

Open the application's database with the new driver name, and pass the same recorder to the test with Recorder:

func TestGetTodo(t *testing.T) {
	dsn := filepath.Join(t.TempDir(), "todos.db")
	seedTodos(t, dsn)

	// the app opens the database through the recording driver
	db, err := sql.Open("sqlite-recorded", dsn)
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()

	apitest.New("gets a todo").
		Recorder(recorder).
		Report(apitest.SequenceDiagram()).
		Handler(newTodoAPI(db)).
		Get("/todos/1").
		Expect(t).
		Status(http.StatusOK).
		Body(`{"id": 1, "title": "write docs", "done": false}`).
		End()
}

// seedTodos uses the plain driver so the setup queries stay out of the diagram.
func seedTodos(t *testing.T, dsn string) {
	db, err := sql.Open("sqlite", dsn)
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()

	_, err = db.Exec(`
		CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT, done BOOLEAN);
		INSERT INTO todos (title, done) VALUES ('write docs', false);
	`)
	if err != nil {
		t.Fatal(err)
	}
}

Each query appears as a SQL Query message with its arguments, followed by a SQL Result message with the number of rows returned or affected. The participant is named after the wrapped driver.

Keep setup out of the diagram

Everything the wrapped driver does is recorded, including queries made before the test runs. The example seeds the database through the plain sqlite driver so that the inserts do not appear in the diagram. Alternatively, call recorder.Reset() after seeding. The recorder is reset after each report, so tests that share it start clean.

Connectors

Drivers that are opened with a driver.Connector, such as pgx through stdlib.GetConnector, can be wrapped with WrapConnectorWithRecorder. The second argument names the participant:

connector := apitestdb.WrapConnectorWithRecorder(pgxConnector, "postgres", recorder)
db := sql.OpenDB(connector)

Examples

The apitest repository has complete examples for SQLite, MySQL and PostgreSQL.

For DynamoDB, see the apitest-dynamodb companion library.

On this page