Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mkeeler/mock-http-api

Go helpers for mocking an HTTP API using stretchr/testify/mock
https://github.com/mkeeler/mock-http-api

Last synced: 19 days ago
JSON representation

Go helpers for mocking an HTTP API using stretchr/testify/mock

Awesome Lists containing this project

README

        

# mock-http-api [![PkgGoDev](https://pkg.go.dev/badge/github.com/mkeeler/mock-http-api)](https://pkg.go.dev/github.com/mkeeler/mock-http-api)
Go helpers for mocking an HTTP API using stretchr/testify/mock

## Library Usage

```go
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://: 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")
}
}
```

## Code Generation

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.

### Installing the code generator

```sh
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_

### Using `mock-api-gen`

```sh
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](#endpoint-options) to view the list of available options to configure mocked endpoints.

The format of the endpoints file is:

```json
{
"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:

```go
// 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:

```go
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
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](#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](#import-options) for more information. |

#### Import Options

To include custom types in the method signature for strict type checking, specify the import package in the endpoints file.

```json
{
"Imports": {
"api": "github.com/namespace/project/path/api"
},
"Endpoints": {
"ListResource": {
"Method": "GET",
"Path": "/resources",
"ResponseFormat": "json",
"ResponseType": "[]*api.Resource"
}
}
}
```

#### Full Usage

```
Usage of mock-api-gen:
mock-api-gen [flags] -type -endpoints [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
```