Go helpers for mocking an HTTP API using stretchr/testify/mock
package mock_test
import (
"encoding/json"
"net/http"
"testing"
mockapi "github.com/mkeeler/mock-http-api"
)
// This test will pass as all the requisite API calls are made.
func TestMyAPI(t *testing.T) {
m := mockapi.NewMockAPI(t)
// http.Get will add both of the headers but we don't want to care about them.
m.SetFilteredHeaders([]string{
"Accept-Encoding",
"User-Agent",
})
// This sets up an expectation that a GET request to /my/endpoint will be made and that it should
// return a 200 status code with the provided map sent back JSON encoded as the body of the response
call := m.WithJSONReply(mockapi.NewMockRequest("GET", "/my/endpoint"), 200, map[string]string{
"foo": "bar",
})
// This sets the call to be required to happen exactly once
call.Once()
// This makes the HTTP request to the mock HTTP server. The mock api server exposes a URL()
// method which will return a string of the form http://<ip>:<port> that you can use to make requests.
// Typically this bit of code below would be replaced with invocation of some function that uses
// your API
resp, err := http.Get(fmt.Sprintf("%s/my/endpoint", m.URL()))
if err != nil {
t.Fatalf("Error issuing GET of /my/endpoint: %v", err)
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
var output map[string]string
if err := dec.Decode(&output); err != nil {
t.Fatalf("Error decoding response: %v", err)
}
if val, ok := output["foo"]; !ok || val != "bar" {
t.Fatalf("Didn't get the expected response")
}
}
The code generator will create a new mock API type with helper methods for all the desired endpoints. These helpers
are meant to be more ergonomic to use that the raw mock-http-api
module itself.
go get github.com/mkeeler/mock-http-api/cmd/mock-api-gen
Note that you may need to run this command with GO111MODULE=on if executing outside of your GOPATH
mock-api-gen -type MockMyAPI -endpoints ./endpoints.json -pkg myapi -output api.helpers.go
This command will take in the JSON file of endpoints and generate the desired type with helpers for mocking responses to each API. See endpoint options to view the list of available options to configure mocked endpoints.
The format of the endpoints file is:
{
"Endpoints": {
"UpdateResource": {
"Method": "POST",
"Path": "/resource/%s",
"PathParameters": ["resourceID"],
"BodyFormat": "json",
"ResponseFormat": "json",
"Headers": true,
"QueryParams": false
}
}
}
Using this as input the following file would be generated:
// Code generated by "mock-expect-gen -type MockAPI -pkg fakeapi -endpoints endpoints.json -output ./api.go"; DO NOT EDIT.
package fakeapi
import (
"fmt"
mockapi "github.com/mkeeler/mock-http-api"
)
type MockAPI struct {
*mockapi.MockAPI
}
func NewMockAPI(t mockapi.TestingT) *MockAPI {
return &MockAPI{
MockAPI: mockapi.NewMockAPI(t),
}
}
func (m *MockConsulAPI) UpdateResource(resourceID string, headers map[string]string, body map[string]interface{}, status int, reply interface{}) *mockapi.MockAPICall {
req := mockapi.NewMockRequest("POST", fmt.Sprintf("/resource/%s", resourceID)).WithBody(body).WithHeaders(headers)
return m.WithJSONReply(req, status, reply)
}
Then when you want to use this you would:
func TestFakeAPI(t *testing.T) {
m := fakeapi.NewMockAPI(t)
// Not necessary when the `t` passed into NewMockAPI supports a Cleanup method. (such as with the Go 1.14 testing.T type)
defer m.Close()
m.UpdateResource("some-id-here",
nil,
map[string]interface{"abc", "def"},
200,
map[string]interface{"abc", "def", "added": true})
httpServerURL := m.URL()
// do something to cause the HTTP API call to happen here.
// nothing else is necessary. Either the deferred m.Close or the automatic testing cleanup
// will assert that the required API calls were made.
}
Endpoint options for generating method signatures:
Argument | Type | Description |
---|---|---|
Method | string |
The HTTP method for the endpoint. |
Path | string |
The path of the endpoint. Include string format verbs to represent path parameters (/v1/resource/%s ). |
PathParameters | []string |
List of path parameters of the endpoint. |
BodyFormat | string |
The format of the body expected for the HTTP request. For example, none, json, string, stream. |
BodyType | string |
A string describing the go type for the method signature to include the typed representation of the request body. The default type is map[string]interface{} . Custom types from other packages, like *api.Resource , are supported. This requires the package to be specified in order to be properly imported. See import options for more information. |
QueryParams | bool |
This includes the option for mocking API query params in the method signature with the type map[string]string . |
Headers | bool |
This includes the option for HTTP headers for the request in the method signature with the type map[string]string . |
ResponseFormat | string |
The format of the response body returned: none, json, string, stream, func. |
ResponseType | string |
A string describing the go type for the method signature to include the typed representation of the response body. The default type is interface{} . Custom types from other packages, like *api.Resource , are supported. This requires the package to be specified in order to be properly imported. See import options for more information. |
To include custom types in the method signature for strict type checking, specify the import package in the endpoints file.
{
"Imports": {
"api": "github.com/namespace/project/path/api"
},
"Endpoints": {
"ListResource": {
"Method": "GET",
"Path": "/resources",
"ResponseFormat": "json",
"ResponseType": "[]*api.Resource"
}
}
}
Usage of mock-api-gen:
mock-api-gen [flags] -type <type name> -endpoints <var name> [package]
Flags:
-endpoints string
File holding the endpoint configuration. (default "endpoints")
-output string
Output file name.
-pkg string
Name of the package to generate methods in
-tag value
Build tags the generated file should have. This may be specified multiple times.
-type string
Method receiver type the mock API helpers should be generated for