httpstub

package module
v0.28.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2026 License: MIT Imports: 32 Imported by: 4

README

httpstub Go Reference Coverage Code to Test Ratio Test Execution Time

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.

Usage

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

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
ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
	ts.Close()
})
ts.ResponseDynamic()
Dynamic response to a specific endpoint
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.

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.
// 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()
// 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.

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.

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
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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Status added in v0.9.0

func Status(pattern string) responseExampleOption

Status specify the example response to use by status code.

Types

type Option added in v0.4.0

type Option func(*config) error

func Addr added in v0.26.0

func Addr(addr string) Option

Addr set server address.

func BasePath added in v0.26.1

func BasePath(basePath string) Option

BasePath set base URL path prefix.

func CACert added in v0.4.1

func CACert(cacert []byte) Option

CACert set CA.

func Certificates added in v0.4.1

func Certificates(cert, key []byte) Option

Certificates set certificates ( cert, key ).

func ClientCACert added in v0.5.0

func ClientCACert(cacert []byte) Option

ClientCACert set client CA.

func ClientCertificates added in v0.5.0

func ClientCertificates(cert, key []byte) Option

ClientCertificates set client certificates ( cert, key ).

func DynamicResponseMode added in v0.28.0

func DynamicResponseMode(mode ResponseMode) Option

DynamicResponseMode sets the response mode for ResponseDynamic. - AlwaysGenerate: Always generate from schema (ignore examples) - default - ExamplesOnly: Use only explicit examples (error if not found) - PreferExamples: Prefer examples, fallback to schema generation.

func OpenApi3 added in v0.8.0

func OpenApi3(l string) Option

OpenApi3 sets OpenAPI Document using file path.

func OpenApi3FromData added in v0.8.0

func OpenApi3FromData(b []byte) Option

OpenApi3FromData sets OpenAPI Document from bytes.

func Seed added in v0.28.0

func Seed(seed int64) Option

Seed sets the seed for random number generation, enabling deterministic responses.

func SkipCircularReferenceCheck added in v0.17.0

func SkipCircularReferenceCheck(skip bool) Option

SkipCircularReferenceCheck sets whether to skip circular reference check in OpenAPI Document.

func SkipValidateRequest added in v0.8.0

func SkipValidateRequest(skip bool) Option

SkipValidateRequest sets whether to skip validation of HTTP request with OpenAPI Document.

func SkipValidateResponse added in v0.8.0

func SkipValidateResponse(skip bool) Option

SkipValidateResponse sets whether to skip validation of HTTP response with OpenAPI Document.

func UseTLS added in v0.4.0

func UseTLS() Option

UseTLS enable TLS.

func UseTLSWithCertificates added in v0.4.0

func UseTLSWithCertificates(cert, key []byte) Option

UseTLSWithCertificates enable TLS with certificates ( cert, key ).

type ResponseMode added in v0.28.0

type ResponseMode int

ResponseMode defines how to generate responses from OpenAPI documents.

const (
	// AlwaysGenerate always generates responses from schemas.
	// Examples are ignored.
	// This is the default behavior.
	AlwaysGenerate ResponseMode = iota
	// ExamplesOnly uses only explicit examples from the OpenAPI document.
	// If no example is found, an error is returned.
	ExamplesOnly
	// PreferExamples prefers examples but falls back to schema generation.
	PreferExamples
)

type Router added in v0.3.0

type Router struct {
	// Set *httptest.Server.URL
	URL string
	// contains filtered or unexported fields
}

func NewRouter

func NewRouter(t TB, opts ...Option) *Router

NewRouter returns a new router with methods for stubbing.

func NewServer added in v0.2.0

func NewServer(t TB, opts ...Option) *Router

NewServer returns a new router including *httptest.Server.

func NewTLSServer added in v0.4.0

func NewTLSServer(t TB, opts ...Option) *Router

NewTLSServer returns a new router including TLS *httptest.Server.

func (*Router) ClearRequests added in v0.6.0

func (rt *Router) ClearRequests()

ClearRequests clear []*http.Request received by router.

func (*Router) Client added in v0.3.0

func (rt *Router) Client() *http.Client

Client returns *http.Client which requests *httptest.Server.

func (*Router) Close added in v0.3.0

func (rt *Router) Close()

Close shuts down *httptest.Server.

func (*Router) DefaultHeader added in v0.3.0

func (rt *Router) DefaultHeader(key, value string)

DefaultHeader append default middleware which append header.

func (*Router) DefaultMiddleware added in v0.3.0

func (rt *Router) DefaultMiddleware(mw func(next http.HandlerFunc) http.HandlerFunc)

DefaultMiddleware append default middleware.

func (*Router) Match added in v0.3.0

func (rt *Router) Match(fn func(r *http.Request) bool) *matcher

Match create request matcher with matchFunc (func(r *http.Request) bool).

func (*Router) Method added in v0.3.0

func (rt *Router) Method(method string) *matcher

Method create request matcher using method.

func (*Router) Path added in v0.3.0

func (rt *Router) Path(path string) *matcher

Path create request matcher using path.

func (*Router) Pathf added in v0.7.0

func (rt *Router) Pathf(format string, a ...any) *matcher

Pathf create request matcher using sprintf-ed path.

func (*Router) Prepend added in v0.18.0

func (rt *Router) Prepend() *Router

Prepend prepend matcher.

func (*Router) Query added in v0.9.0

func (rt *Router) Query(key, value string) *matcher

Query create request matcher using query.

func (*Router) Requests added in v0.3.0

func (rt *Router) Requests() []*http.Request

Requests returns []*http.Request received by router.

func (*Router) ResponseDynamic added in v0.28.0

func (rt *Router) ResponseDynamic(opts ...responseExampleOption)

ResponseDynamic set handler which return response from OpenAPI v3 Document. The response mode is determined by the Router's responseMode.

func (*Router) ServeHTTP added in v0.3.0

func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Router) Server added in v0.3.0

func (rt *Router) Server() *httptest.Server

Server returns *httptest.Server with *Router set.

func (*Router) TLSServer added in v0.4.0

func (rt *Router) TLSServer() *httptest.Server

TLSServer returns TLS *httptest.Server with *Router set.

type TB added in v0.8.0

type TB interface {
	Error(args ...any)
	Errorf(format string, args ...any)
	Fatal(args ...any)
	Fatalf(format string, args ...any)
	Helper()
}

Directories

Path Synopsis
Package mock_httpstub is a generated GoMock package.
Package mock_httpstub is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL