Giter VIP home page Giter VIP logo

handlertest's Introduction

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.

handlertest's People

Contributors

epels avatar

Stargazers

 avatar

Watchers

 avatar  avatar

Forkers

bonomali

handlertest's Issues

Add support for request headers

The package should offer the possibility to set support headers for requests. A very logical use-case for this is setting Authorization on a request.

I'm considering two implementations here:

  1. Simply have a Headers field on the Request struct.
  2. Add a type BeforeFunc func(*http.Request), and allow these to somehow be passed to Run() in a variadic way. These will all be invoked and provided with the *http.Request before it's sent. It can then modify the request to prepare it in all sorts of ways.

An obvious pro to approach (2) is that it offers a lot more flexibility. Not only can headers be set here, but everything about the request can be modified. However: this approach only works when test cases are defined in code. This would not work with test cases from YAML, and that is a constraint that makes me lean towards the first option.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.