If you've ever written functional tests for a Go HTTP server, you’ve probably found yourself stuck between two extremes:
- Writing black-box tests with curl or Postman clones (slow, no debug, hard to maintain)
- Writing verbose Go test code for every API call, mocking, assertions, cleanup, etc.
That’s why I created testy — a declarative testing framework for Go HTTP APIs, built on top of YAML and your actual http.Handler.
Why testy?
Here’s what makes testy different:
- Declarative tests written in plain YAML
-
Real
http.Handlerexecution — your actual app code, not a subprocess - Full PostgreSQL fixture support via
pgfixtures - Powerful mocking, assertions, and SQL checks
- Great for debugging and stepping through real code
Debug Your Tests Like Real Code
Because testy runs requests through your actual Go http.Handler, you can set breakpoints in your API handlers and run tests in your IDE’s debug mode (go test -v -run <TestName>).
This makes test runs feel like regular request handling — not like opaque black-box e2e scripts.
You don’t need to spin up your app on a port. testy hits your router directly — whether it’s from net/http, Gin, Chi, Echo or anything else.
Declarative Testing With YAML
Test scenarios are written as YAML lists of steps:
- name: get_user
request:
method: GET
path: /users/123
response:
status: 200
headers:
Content-Type: application/json
json: |
{
"id": 123,
"name": "Alice"
}
No Go code. No boilerplate. Just requests and expectations.
Dynamic Placeholders and Context
testy supports smart templating:
-
{{step.response.field}}— reference values from earlier steps -
{{ENV_VAR}}— use environment variables (likeUUID, tokens, etc.) - Used in URLs, headers, bodies, SQL queries, everywhere
Example:
- name: get user
request:
path: /users/{{create_user.response.id}}
Works with Your Real Code
A minimal Go test using testy looks like this:
testy.Run(t, &testy.Config{
Handler: api.Router(), // your real app router
ConnStr: os.Getenv("TEST_DB"), // PostgreSQL
CasesDir: "./tests/cases", // YAML test cases
FixturesDir: "./tests/fixtures", // pgfixtures
})
That’s it.
You can run it with:
go test ./...
And debug it with your IDE as usual. Put a breakpoint in your handler — it just works.
Real-Life Example: Creating a Project
From create.yml:
- name: create project with team
fixtures:
- empty_db
- developers_team
steps:
- name: login
request:
method: POST
path: /auth
headers:
Content-Type: application/json
body: {"username": "admin", "password": "Warden123!"}
response:
status: 200
headers:
Content-Type: application/json
- name: add_project
request:
method: POST
path: /projects/add
headers:
Authorization: "Bearer {{login.response.access_token}}"
Content-Type: application/json
body:
name: "Project With Team"
team_id: 1
response:
status: 201
dbChecks:
- query: SELECT name FROM projects WHERE team_id = 1
result:
- name: "Project With Team"
No test logic in Go. It’s all in the scenario.
Powered by pgfixtures
Each scenario can load fixtures into the DB before it starts — using pgfixtures.
Fixtures are described in YAML, and support:
- Truncating tables
- Resetting sequences
-
$eval(SELECT ...)expressions
public.users:
- id: 1
name: "Alice"
created_at: $eval(SELECT NOW())
Mocks for Outgoing Requests
You can define HTTP mocks directly in the test file:
mockServers:
notification:
routes:
- method: POST
path: /send
response:
status: 202
headers:
Content-Type: application/json
json: '{"status":"queued"}'
mockCalls:
- mock: notification
count: 1
expect:
method: POST
path: /send
body:
contains: "Joseph"
Mocks are verified and auto-spun for you. No third-party servers needed.
Highlights
- Pure YAML: easily readable, editable by devs and QA alike
- Real code path: debug API as usual — same context, same stacktrace
- Built-in mocks: test integrations and verify outbound calls
- DB fixtures & assertions: validate everything inside and out
- Fast, IDE-friendly, minimal
Perfect for:
- Testing REST API behavior end-to-end
- Validating JSON responses and DB state
- Reproducing edge cases and regression bugs
- Debugging logic by stepping through real code paths
Try It Out
go get github.com/rom8726/testy@latest
Set up your test directory like:
/tests
├─ cases/ # YAML test scenarios
├─ fixtures/ # YAML DB fixtures
Then run go test — and debug away.
Open Source
testy is Apache-2.0 licensed and open to contributions.
If you like writing tests like a human, check it out:
GitHub: github.com/rom8726/testy
Top comments (0)