rom8726.github.io

testy

A tiny functional-testing framework for HTTP handlers written in Go.

Github: github.com/rom8726/testy

It lets you describe end-to-end scenarios in YAML, automatically:

Only PostgreSQL is supported.

Installation

go get github.com/rom8726/testy@latest

Quick example

Directory layout:

/project
 ├─ api/                 # your application code
 ├─ tests/
 │   ├─ cases/           # *.yml files with scenarios
 │   ├─ fixtures/        # *.yml fixtures for pgfixtures
 └── api_test.go         # Go test that calls testy.Run

1. Multi-step YAML case (tests/cases/user_flow.yml)

- name: end-to-end user flow
  fixtures:
    - users

  steps:
    - name: create_user
      request:
        method: POST
        path:   /users
        body:
          name:  "Alice"
          email: "alice@example.com"
      response:
        status: 201
        json: |
          {
            "id":        "<<PRESENCE>>",
            "name":      "Alice",
            "email":     "alice@example.com"
          }

    - name: get user
      request:
        method: GET
        path:   /users/{{create_user.response.id}}     # pulls "id" from the previous response
      response:
        status: 200
        json: |
          {
            "id":   "{{create_user.response.id}}",
            "name": "Alice"
          }

    - name: update email
      request:
        method: PATCH
        path:   /users/{{create_user.response.id}}
        headers:
          X-Request-Id: "{{UUID}}"            # env-var substitution
        body:
          email: "alice+new@example.com"
      response:
        status: 204
      dbChecks:
        - query:  SELECT email FROM users WHERE id = {{create_user.response.id}}
          result: '[{ "email":"alice+new@example.com" }]'

How the placeholders work:

Placeholders are resolved in the request URL, headers and body, as well as inside dbChecks.query.

2. Go test (tests/api_test.go)

package project_test

import (
  "os"
  "testing"

  "project/api"
  "github.com/rom8726/testy"
)

func TestAPI(t *testing.T) {
  connStr := os.Getenv("TEST_DB") // postgres://user:password@localhost:5432/db?sslmode=disable

  testy.Run(t, &testy.Config{
    Handler:     api.Router(),    // your http.Handler
    CasesDir:    "./cases",
    FixturesDir: "./fixtures",
    ConnStr:     connStr,
  })
}

Run the tests:

go test ./...

Features

Declarative scenarios

PostgreSQL fixtures

Loaded with rom8726/pgfixtures:

Hooks

Optional pre/post request hooks to stub time, clean caches, etc.:

testy.Run(t, &testy.Config{
    // ...
    BeforeReq: func() error { /* do something */ return nil },
    AfterReq:  func() error { /* do something */ return nil },
})

Zero reflection magic

The framework only needs:

YAML reference

- name: string

  fixtures:            # optional, order matters
    - fixture-file     # without ".yml" extension

  steps:
    - name: string

      request:
        method:  GET | POST | PUT | PATCH | DELETE | ...
        path:    string            # placeholders {{...}} allowed
        headers:                   # optional
          X-Token: "{{TOKEN}}"
        body:                      # optional, any YAML\JSON
          userId: "123"

      response:
        status: integer
        json:   string             # optional, must be valid JSON

      dbChecks:                    # optional, list
        - query:  SQL string       # placeholders {{...}} allowed
          result: JSON|YAML        # expected rows as JSON array

Why another testing tool?

License

Apache-2.0 License