# Testing plugins

> Test plugins with go test, run database tests against real PostgreSQL containers, replay API parity fixtures and keep documentation examples running.

SummerCMS uses the standard Go test tooling. There is no separate test runner and no PHPUnit bootstrap: a plugin's tests are `_test.go` files next to its code, and `go test` runs them.

## Running tests

Run every test in the module from its root:

```sh
go vet ./...
go test ./...
```

Tests that need Docker, such as database tests, skip themselves in short mode. Use it for a fast loop:

```sh
go test -short ./...
```

In an application with local plugins in a `go.work` workspace, run the tests of one plugin by its directory, for example `go test ./plugins/blog/...`.

## Unit tests without a database

Most plugin code runs without a database. Build a container with `backpack.New`, call your plugin's Register and Boot, and assert on what it published. Drive HTTP handlers with `net/http/httptest`: `surf.Assemble` builds the same handler `serve` uses, so a test can send requests to your routes without listening on a port. Call console commands in-process with `bonfire.Call`.

## Database tests

SummerCMS supports PostgreSQL only, so database tests run against real PostgreSQL rather than an SQLite stand-in. The framework's own tests start a `postgres:16-alpine` container through testcontainers-go and create a fresh database per test. Follow the same pattern in plugin tests:

- skip the test when `testing.Short` reports true;
- migrate a fresh database per test with `lagoon.Migrate`, so tests do not depend on each other.

Docker must be running for these tests.

## API parity tests

When you port an existing backend, its real responses are the acceptance test. [tide](/docs/api/tide.md) records request and response fixtures from the reference backend and replays them against your port, reporting differences after masking IDs and timestamps. The `summer parity:record`, `summer parity:replay`, `summer parity:proxy` and `summer parity:broadcasts` commands wrap it.

## Examples in the documentation

Every Go code block in these docs is a copy of an `Example` function or a marked region of a test that `go test ./...` runs. If you change a framework API and forget an example, `go test` fails. Write your plugin's examples the same way: an `Example` function with an `// Output:` comment is compiled, run and compared by `go test`, so it cannot go stale.
