handlertest

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 27, 2020 License: MIT Imports: 10 Imported by: 0

README

handlertest GoDoc

Go has great test tooling. Althoughhttptest makes testing your HTTP handlers convenient, it can still result in verbose test code. This package, handlertest, seamlessly integrates with the Go tools you already know, but removes that boilerplate.

Tests define what request to send, and what response is expected. This can be done in either (Go) code, or in YAML. It runs within the process itself and runs against any http.Handler, which gives you the flexibility to wire up your service as you would in any other tests.

Example

Let's say we're testing a handler that exposes a simple health check endpoint, which looks like this:

func handler() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/health", health)
	return mux
}

func health(w http.ResponseWriter, r *http.Request) {
	_, _ = w.Write([]byte("ok"))
}

To test this, we would normally have to manually construct a httptest.ResponseRecorder and *http.Request. We would then manually pass this to our handler's ServeHTTP method, and then assert the relevant aspects of the response. This is a lot easier to do with handlertest though. We'll now look at the two approaches - code, and YAML.

Code
func TestHealth(t *testing.T) {
	h := handler()
	handlertest.Run(t, h, handlertest.TestCase{
		Name: "Health returns OK",
		Request: handlertest.Request{
			Method: http.MethodGet,
			URL:    "/health",
		},
		Response: handlertest.Response{
			Code: http.StatusOK,
			Body: "ok",
		},
	})
}

Run() is variadic, so any number of test cases can be passed.

YAML

It's even easier to do this with some straightforward YAML. Here's an example with a couple more test cases:

# testdata/health.yaml
- name: "Health returns OK"
  request:
    method: "GET"
    url: "/health"
  response:
    body: "ok"
- name: "Health returns bad method on POST"
  request:
    method: "POST"
    url: "/health"
  response:
    code: 405
- name: "Router returns not found on undefined URL"
  request:
    method: "GET"
    url: "/health/foo"
  response:
    code: 404

From our test code, we'll only need to point handlertest towards this YAML file:

func TestHealth(t *testing.T) {
	h := handler()
	handlertest.RunFromYAML(t, h, "testdata/health.yaml")
}

To make this as painless as possible, you won't even have to deal with opening and parsing the file. If something unexpected happens, e.g. the YAML cannot be parsed, the test will be marked as failed with a descriptive error message.

Running the test cases defined in this YAML file against the handler we created above yields the following result:

pels$ go test -v ./...
=== RUN   TestHealth
=== RUN   TestHealth/Health_returns_OK
=== RUN   TestHealth/Health_returns_bad_method_on_POST
=== RUN   TestHealth/Router_returns_not_found_on_undefined_URL
--- FAIL: TestHealth (0.00s)
    --- PASS: TestHealth/Health_returns_OK (0.00s)
    --- FAIL: TestHealth/Health_returns_bad_method_on_POST (0.00s)
        handlertest.go:92: Got response code 200, expected 405
    --- PASS: TestHealth/Router_returns_not_found_on_undefined_URL (0.00s)
FAIL
FAIL	github.com/epels/example	0.022s
FAIL

As you can see, this package plays nicely with the Go test tool.

Credits

This project depends on the excellent go-yaml/yaml package.

Documentation

Overview

Package handlertest lets you define test cases in concise code, or in YAML, and runs these against any HTTP handler.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Run

func Run(t tt, h http.Handler, tcs ...TestCase)

Run runs the test cases, tcs, against h. When the response does not match the expectation, t is flagged as failed with a descriptive error.

func RunFromYAML

func RunFromYAML(t tt, h http.Handler, path string)

RunFromYAML reads a YAML serialized representation of TestCases from path and runs them against h. For locating path, the normal rules from os.Open are followed. If the file at path cannot be located or parsed, execution is stopped. If the response does not match the expectation, t is flagged as failed with a descriptive error.

Types

type Request

type Request struct {
	Method  string
	URL     string
	Body    string
	Headers []string
}

Request describes the request to fire at the HTTP handler.

type Response

type Response struct {
	// Code is the expected HTTP status code.
	Code int
	// Body is the expected response body.
	Body string
}

Response describes the expected response from the HTTP handler. All fields are optional: if they are not set, these are not asserted.

type TestCase

type TestCase struct {
	// Name can optionally be set to easily identify the test within the
	// Go test tool's output.
	Name     string
	Request  Request
	Response Response
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL