test2doc: Generate Your API Docs
API docs are important. We all know that. They are also painful and tedious to maintain. When your docs aren’t accurate, you get more questions. And everyone loses time.
I’ve always thought of unit tests as a great source of documentation. But the non-Go engineers consuming my API don’t often agree.
Yet most of the same information is there:
- request format:
- HTTP method
- URI
- query params
- request headers
- request body
- response format:
- status code
- response headers
- response body
All that’s missing from this list is the
high-level descriptions, the context.
Enter Go doc.
We can find descriptions of an HTTP handler in its Go doc string, eg:
|
|
And thus began test2doc - automatically generate complete API
documentation from your existing Go unit tests + Go doc strings.
Example
Given an HTTP handler func, eg.:
|
|
And a test for this handler func, eg.:
|
|
Test2doc will automatically generate markdown documentation for this endpoint in the API Blueprint format as your tests run, like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Group widgets ## /widgets/{id} + Parameters + id: `2` (number) ### Get Widget [GET] retrieves a single Widget + Response 200 + Body { "Id": 2, "Name": "Pencil", "Role": "Utensil" } |
Which you can then parse and host w/ Apiary.io:
Or use a custom parser and host yourself.
Getting started with test2doc
A big goal of mine as I was writing this project was to limit the amount of additional code needed to get test2doc up and running.
Requirements:
- You must have a
TestMain
for each of the packages you wish to document. - All of the tests in your package must share a single
test.Server
instance (from test2doc/test package)
3 Code Additions
3 additions, and only to your testing code:
|
|
Some example URLVarExtractor
s for different routers:
gorilla/mux
:
|
|
julienschmidt/httprouter
:
|
|
How the magic happens
Recording Requests and Responses:
Recording the requests and responses was pretty straight-forward:
- Write the HTTP handler’s response initially to an
http.ResponseRecorder
- Add the response header/body to the documentation
- Copy the response back to the original
http.ResponseWriter
- Continue executing the test
Fetching the handler’s Go doc string:
First, let’s take a look at the http.ResponseWriter
interface
from Go’s net/http
package:
|
|
test2doc has its own ResponseWriter
, which implements
http.ResponseWriter
, and looks something like this:
|
|
test2doc’s ResponseWriter
implements the Header
and
WriteHeader
methods by just falling back to those of its
embedded httptest.ResponseRecorder
:
|
|
The magic is in the Write
implementation.
Since every HTTP handler will call the Write
method at
some point to write out the response, we hijack our
ResponseWriter
’s Write
to inspect the call stack and find
our HTTP handler before performing the Write
:
|
|
Here, setHandlerInfo
iterates up the call stack (using Go’s runtime
package) until it finds a caller whose declaration is inside the
package we are testing (in all likelihood, the handler).
Once we have the handler’s function name, we can get the Go doc string
using the go/doc
package.
Thoughts for the future
I’d like to convert the main types to interfaces, allowing support for formats other than the API Blueprint format.
I’d also like to improve upon the “handler-finding” algorithm (above), to make it more reliably accurate.
Contributions welcome!
Drop me a line at sadams.codes@gmail.com, or adams-sarah on github.