Building Microservices in Go: REST APIs - Testing
Tags: go, microservices, testing
Published: 2021-06-04 12:21:36

> **Disclaimer:** This post includes Amazon affiliate links. If you click on one of them and you make a purchase I'll earn a commission. Please notice your final price **is not** affected at all by using those links.

---

## Implementing tests

When we implemented [HTTP Handlers](https://mariocarrion.com/2021/04/18/golang-microservices-rest-api-http-handlers.html) last time we mentioned a few difficult things to do when using the standard library, like pattern matching or nested resources, testing handlers in Go on the other hand requires no third party packages, this is because of [`net/http/httptest`](https://pkg.go.dev/net/http/httptest) which is already included in the standard library.

Using `net/http/httptest` in the simplest form works like this:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
)

func main() {
	handler := func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "<html><body>net/http/httptest</body></html>")
	}

	req := httptest.NewRequest("GET", "http://test.com/foo", nil)
	w := httptest.NewRecorder()
	handler(w, req)

	resp := w.Result()
	body, _ := io.ReadAll(resp.Body) // XXX: Explicitly ignoring errors

	fmt.Printf("%d %s %s", resp.StatusCode, resp.Header.Get("Content-Type"), string(body))
}
```

Where a `httptest.ResponseRecorder` acts as a writer when requesting a handler, this argument will in the end record the results being written to and it will allow us to test we are trying to implement.

## Structuring HTTP tests

> The code used for this post is [available on Github](https://github.com/MarioCarrion/todo-api-microservice-example/tree/9b6a5c21116032735089c6340ccc7c6bb6a240c8).

Because of the way we have been [implementing our handlers already](https://mariocarrion.com/2021/03/21/golang-microservices-domain-driven-design-project-layout.html), the structure I like to follow when implementing tests is the following:

```go
func Test<Name>_<HTTPVerb>(t *testing.T) {
	t.Parallel()

	type output struct {
		expectedStatus int
		expected       interface{}
		target         interface{}
	}

	tests := []struct {
		name   string
		setup  func(*resttesting.Fake<Task>Service)
		input  []byte
		output output
	}{
		{
			// ...
		},
	}

	for _, tt := range tests {
		tt := tt

		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			router := mux.NewRouter()
			svc := &resttesting.Fake<Task>Service{}
			tt.setup(svc)

			rest.New<Task>Handler(svc).Register(router)

			//-

			res := doRequest(router,
				httptest.NewRequest(http.MethodPost, "/<name>", bytes.NewReader(tt.input)))

			//-

			assertResponse(t, res, test{tt.output.expected, tt.output.target})

			if tt.output.expectedStatus != res.StatusCode {
				t.Fatalf("expected code %d, actual %d", tt.output.expectedStatus, res.StatusCode)
			}
		})
	}
}
```

The idea is:

1. Define an `output` struct type, meant to represent the data returned back in the response, this means the status code, response and the target type to use for unmarshaling.
1. I like defining a helper test function, something like [`assertResponse`](https://github.com/MarioCarrion/todo-api-microservice-example/blob/9b6a5c21116032735089c6340ccc7c6bb6a240c8/internal/rest/task_test.go#L310-L321), to compare the body results to an expected unmarshaled value.
1. The tests table will define tests using the usual `input` and `output` as well as a new `setup` function that will initialize our mock type,
1. Finally our actual test will connect all the dots and call the initializer, do the request and compare the results.

Feel free to review the [implement tests in the repository](https://github.com/MarioCarrion/todo-api-microservice-example/blob/9b6a5c21116032735089c6340ccc7c6bb6a240c8/internal/rest/task_test.go#L19-L121) for more concrete details.

## Parting words

Implementing tests for HTTP Handlers requires a few steps, is easier than implementing handler and in practice it should not require third party packages to accomplish what we are trying to confirm, if anything perhaps using a package for equality like [github.com/google/go-cmp](https://mariocarrion.com/2021/01/22/go-package-equality-google-go-cmp.html) should be more than enough.

In the next post I will cover implementing a custom type that represents the type used for a field, the idea is to improve our API and make it more user friendly way for our users.

## Recommended Reading

If you're looking to sink your teeth into more REST and Web Programming I recommend the following books:

* [The Design of Web APIs (2019)](https://amzn.to/32yFwFe)
* [Go Web Programming (2016)](https://amzn.to/30euJ1U)
* [REST in Practice (2010)](https://amzn.to/3n6ztRZ)