melatonin
melatonin is a fluent, flexible REST API testing library for Go. It provides many of the benefits of a domain-specific test language but with the flexibililty of writing pure Go. Use it to write unit tests that test your http.Handler
s routes directly, or E2E tests that target routes on a running service written in any language.
See the full user guide and the API documentation for more information.
Installation
go get github.com/jefflinse/melatonin/mt
Usage
Just create a context and write tests. The test runner will run the tests and return the results.
func main() {
myAPI := mt.NewURLContext("http://example.com")
results := mt.RunTests([]mt.TestCase{
myAPI.GET("/resource", "Fetch a record successfully").
ExpectStatus(200).
ExpectBody("Hello, world!"),
})
mt.PrintResults(results)
}
With the above, you'll get a nicely formatted table of results.
$ go run example.go
1 ✔ Fetch a record successfully GET /foo 3.9252ms
1 passed, 0 failed, 0 skipped in 3.9252ms
When run as a regular Go test, results will be reported through the standard testing.T
context.
package mypackage_test
import (
"testing"
"github.com/jefflinse/melatonin/mt"
)
func TestAPI(t *testing.T) {
myAPI := mt.NewURLContext("http://example.com")
mt.RunTestsT(t, []mt.TestCase{
myAPI.GET("/resource", "Fetch a record successfully").
ExpectStatus(200).
ExpectBody("Hello, world!"),
})
}
$ go test
PASS
ok github.com/my/api 0.144s
Examples
See the examples directory for full, runnable examples.
Test a service running locally or remotely (E2E tests)
myAPI := mt.NewURLContext("http://example.com")
mt.RunTests(...)
Test a Go HTTP handler directly (unit tests)
myAPI := mt.NewHandlerContext(http.NewServeMux())
mt.RunTests(...)
Define tests
myAPI := mt.NewURLContext("http://example.com")
tests := []mt.TestCase{
myAPI.GET("/resource").
ExpectStatus(200).
ExpectBody(String("Hello, World!")),
myAPI.POST("/resource").
WithBody(Object{
"name": "Burt Macklin",
"age": 32,
}).
ExpectStatus(201),
myAPI.DELETE("/resource/42").
ExpectStatus(204),
}
Use a custom HTTP client for requests
client := &http.Client{}
myAPI := mt.NewURLContext("http://example.com").WithHTTPClient(client)
Use a custom timeout for all tests
timeout := time.Duration(5 * time.Second)
myAPI := mt.NewURLContext("http://example.com").WithTimeout(timeout)
Specify a timeout for a specific test
myAPI.GET("/resource").
WithTimeout(5 * time.Second).
ExpectStatus(200).
Specify query parameters for a test
Inline:
myAPI.GET("/resource?first=foo&second=bar")
Individually:
myAPI.GET("/resource").
WithQueryParam("first", "foo").
WithQueryParam("second", "bar")
All At Once:
myAPI.GET("/resource").
WithQueryParams(url.Values{
"first": []string{"foo"},
"second": []string{"bar"},
})
Allow or disallow further tests to run after a failure
runner := mt.NewURLContext("http://example.com").WithContinueOnFailure(true)
Create a test case with a custom HTTP request
req, err := http.NewRequest("GET", "http://example.com/resource", nil)
myAPI.DO(req).
ExpectStatus(200)
Expect exact headers and JSON body content
Any unexpected headers or JSON keys or values present in the response will cause the test case to fail.
myAPI.GET("/resource").
ExpectExactHeaders(http.Header{
"Content-Type": []string{"application/json"},
}).
ExpectExactBody(mt.Object{
"foo": "bar",
})
Load expectations for a test case from a golden file
myAPI.GET("/resource").
ExpectGolden("path/to/file.golden")
Golden files keep your test definitions short and concise by storing expectations in a file. See the golden file format specification.
Planned Features
- Output test results in different formats (e.g. JSON, XML, YAML)
- Generate test cases from an OpenAPI specification
See the full V1 milestone for more.
Contributing
Please open an issue if you find a bug or have a feature request.
License
MIT License (MIT) - see LICENSE
for details.