https://github.com/k1low/httpstub
httpstub provides router ( http.Handler ), server ( *httptest.Server ) and client ( *http.Client ) for stubbing, for testing in Go.
https://github.com/k1low/httpstub
go stub stub-server testing
Last synced: 4 months ago
JSON representation
httpstub provides router ( http.Handler ), server ( *httptest.Server ) and client ( *http.Client ) for stubbing, for testing in Go.
- Host: GitHub
- URL: https://github.com/k1low/httpstub
- Owner: k1LoW
- License: mit
- Created: 2022-05-16T08:12:53.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2025-04-14T13:26:17.000Z (about 1 year ago)
- Last Synced: 2025-04-14T14:23:30.423Z (about 1 year ago)
- Topics: go, stub, stub-server, testing
- Language: Go
- Homepage:
- Size: 212 KB
- Stars: 12
- Watchers: 3
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# httpstub [](https://pkg.go.dev/github.com/k1LoW/httpstub)   
httpstub provides router ( `http.Handler` ), server ( `*httptest.Server` ) and client ( `*http.Client` ) for stubbing, for testing in Go.
There is an gRPC version stubbing tool with the same design concept, [grpcstub](https://github.com/k1LoW/grpcstub).
## Usage
``` go
package myapp
import (
"io"
"net/http"
"testing"
"github.com/k1LoW/httpstub"
)
func TestGet(t *testing.T) {
ts := httpstub.NewServer(t)
t.Cleanup(func() {
ts.Close()
})
ts.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)
res, err := http.Get(ts.URL + "/api/v1/users/1")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
res.Body.Close()
})
body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
got := string(body)
want := `{"name":"alice"}`
if got != want {
t.Errorf("got %v\nwant %v", got, want)
}
if len(ts.Requests()) != 1 {
t.Errorf("got %v\nwant %v", len(ts.Requests()), 1)
}
}
```
or
``` go
package myapp
import (
"io"
"net/http"
"testing"
"github.com/k1LoW/httpstub"
)
func TestGet(t *testing.T) {
r := httpstub.NewRouter(t)
r.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)
ts := r.Server()
t.Cleanup(func() {
ts.Close()
})
res, err := http.Get(ts.URL + "/api/v1/users/1")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
res.Body.Close()
})
body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
got := string(body)
want := `{"name":"alice"}`
if got != want {
t.Errorf("got %v\nwant %v", got, want)
}
if len(r.Requests()) != 1 {
t.Errorf("got %v\nwant %v", len(r.Requests()), 1)
}
}
```
## Dynamic Response
httpstub can return responses dynamically using the OpenAPI v3 Document schema.
### Dynamic response to all requests
``` go
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
ts.Close()
})
ts.ResponseDynamic()
```
### Dynamic response to a specific endpoint
``` go
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
ts.Close()
})
ts.Method(http.MethodGet).Path("/api/v1/users/1").ResponseDynamic()
```
### Use specific status code in the response
It is possible to specify status codes using wildcard.
``` go
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
ts.Close()
})
ts.Method(http.MethodPost).Path("/api/v1/users").ResponseDynamic(httpstub.Status("2*"))
```
### Response modes
httpstub supports three response modes that control how responses are generated:
- **AlwaysGenerate** (default): Always generates responses from schemas. Examples in the OpenAPI document are ignored.
- **ExamplesOnly**: Uses only explicit examples from the OpenAPI document. If no example is found, an error is returned.
- **PreferExamples**: Prefers examples but falls back to schema generation if no example is found.
``` go
// Use examples only (error if not found)
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"), httpstub.DynamicResponseMode(httpstub.ExamplesOnly))
t.Cleanup(func() {
ts.Close()
})
ts.ResponseDynamic()
```
``` go
// Prefer examples, fallback to schema generation
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"), httpstub.DynamicResponseMode(httpstub.PreferExamples))
t.Cleanup(func() {
ts.Close()
})
ts.ResponseDynamic()
```
### Deterministic response generation
Use the `Seed` option for deterministic response generation.
``` go
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"), httpstub.Seed(12345))
t.Cleanup(func() {
ts.Close()
})
ts.ResponseDynamic()
```
### HTTP Client that always makes HTTP request to stub server
It is possible to create a client that will always make an HTTP request to the stub server.
``` go
ts := httpstub.NewServer(t)
t.Cleanup(func() {
ts.Close()
})
ts.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)
tc := ts.Client()
res, err := tc.Get("https://example.com/api/v1/users/1") // Request goes to stub server instead of https://example.com
if err != nil {
t.Fatal(err)
}
```
## Example
### Stub Twilio
``` go
package client_test
import (
"net/http"
"testing"
"github.com/k1LoW/httpstub"
twilio "github.com/twilio/twilio-go"
twclient "github.com/twilio/twilio-go/client"
api "github.com/twilio/twilio-go/rest/api/v2010"
)
func TestTwilioClient(t *testing.T) {
r := httpstub.NewRouter(t)
r.Method(http.MethodPost).Path("/2010-04-01/Accounts/*/Messages.json").ResponseString(http.StatusCreated, `{"status":"sending"}`)
ts := r.Server()
t.Cleanup(func() {
ts.Close()
})
tc := ts.Client()
accountSid := "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
authToken := "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
client := twilio.NewRestClientWithParams(twilio.ClientParams{
Client: &twclient.Client{
Credentials: twclient.NewCredentials(accountSid, authToken),
HTTPClient: tc,
},
})
params := &api.CreateMessageParams{}
params.SetTo("08000000000")
params.SetFrom("05000000000")
params.SetBody("Hello there")
res, err := client.ApiV2010.CreateMessage(params)
if err != nil {
t.Error(err)
}
got := res.Status
want := "sending"
if *got != want {
t.Errorf("got %v\nwant %v", *got, want)
}
}
```
## Alternatives
- [github.com/jharlap/httpstub](https://github.com/jharlap/httpstub): Easy stub HTTP servers for testing in Go