Documentation
¶
Overview ¶
Package httpxtest provides a set of utilities for testing HTTP servers.
Index ¶
- type ExhaustBehavior
- type Option
- type ResponseFunc
- type SequencedResponse
- type ServerBuilder
- func (sb *ServerBuilder) Build() *httptest.Server
- func (sb *ServerBuilder) BuildTLS() *httptest.Server
- func (sb *ServerBuilder) On(statusCode int, response any, options ...Option) *ServerBuilder
- func (sb *ServerBuilder) OnFunc(handler http.HandlerFunc, options ...Option) *ServerBuilder
- func (sb *ServerBuilder) OnRoute(method string, route string, statusCode int, response any, options ...Option) *ServerBuilder
- func (sb *ServerBuilder) OnRouteFunc(method string, route string, handler http.HandlerFunc, options ...Option) *ServerBuilder
- func (sb *ServerBuilder) OnRouteSequence(method, route string, exhaust ExhaustBehavior, responses ...SequencedResponse) *ServerBuilder
- func (sb *ServerBuilder) OnSequence(exhaust ExhaustBehavior, responses ...SequencedResponse) *ServerBuilder
Examples ¶
- NewServerBuilder
- NewServerBuilder (ServerLevelOptions)
- ServerBuilder.BuildTLS
- ServerBuilder.On
- ServerBuilder.OnFunc
- ServerBuilder.OnRoute
- ServerBuilder.OnRouteFunc
- ServerBuilder.OnRouteSequence
- ServerBuilder.OnSequence
- ServerBuilder.OnSequence (ExhaustCycle)
- WithContentType
- WithCookie
- WithDelay
- WithHeader
- WithHeaders
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ExhaustBehavior ¶ added in v1.3.0
type ExhaustBehavior int
ExhaustBehavior controls how a sequence of responses is exhausted.
const ( // ExhaustCycle wraps around to the first entry (default). ExhaustCycle ExhaustBehavior = iota // ExhaustRepeatLast repeats the final entry indefinitely. ExhaustRepeatLast ExhaustBehavior = iota // ExhaustServerError returns 500. ExhaustServerError )
type Option ¶
type Option func(w http.ResponseWriter, r *http.Request)
Option is a function that configures how the test server should respond.
func WithContentType ¶
WithContentType sets the Content-Type header on the response.
Example ¶
t := &testing.T{}
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "Horton"}, httpxtest.WithContentType("application/go-snk")).
Build()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.Header.Get("Content-Type"))
Output: application/go-snk
func WithCookie ¶
WithCookie sets a cookie on the response.
Example ¶
t := &testing.T{}
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "Horton"}, httpxtest.WithCookie("session", "abc123")).
Build()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.Header.Get("Set-Cookie"))
Output: session=abc123
func WithDelay ¶
WithDelay adds a delay to the response.
Example ¶
t := &testing.T{}
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "slow"}, httpxtest.WithDelay(10*time.Millisecond)).
Build()
start := time.Now()
_, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(time.Since(start) >= 10*time.Millisecond)
Output: true
func WithHeader ¶
WithHeader sets a header on the response.
Example ¶
t := &testing.T{}
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "Horton"}, httpxtest.WithHeader("X-Custom", "go-snk")).
Build()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.Header.Get("X-Custom"))
Output: go-snk
func WithHeaders ¶
WithHeaders sets multiple headers on the response.
Example ¶
t := &testing.T{}
headers := http.Header{}
headers.Set("X-One", "1")
headers.Set("X-Two", "2")
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "Horton"}, httpxtest.WithHeaders(headers)).
Build()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.Header.Get("X-One"), resp.Header.Get("X-Two"))
Output: 1 2
func WithJSONContentType ¶ added in v1.2.9
func WithJSONContentType() Option
WithJSONContentType sets the Content-Type header to "application/json".
type ResponseFunc ¶ added in v1.3.0
type ResponseFunc func(w http.ResponseWriter, r *http.Request)
ResponseFunc is a function that can be used to respond to an HTTP request.
type SequencedResponse ¶ added in v1.3.0
type SequencedResponse struct {
// contains filtered or unexported fields
}
SequencedResponse is a single entry in a sequence of canned responses.
type ServerBuilder ¶
type ServerBuilder struct {
// contains filtered or unexported fields
}
ServerBuilder is a builder for HTTP servers.
func NewServerBuilder ¶
func NewServerBuilder(t *testing.T, options ...Option) *ServerBuilder
NewServerBuilder creates a new ServerBuilder.
Example ¶
t := &testing.T{}
// On sets the handler for any request that does not match a route.
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "Horton"}).
Build()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.StatusCode, resp.Result.Name)
Output: 200 Horton
Example (ServerLevelOptions) ¶
t := &testing.T{}
// Options passed to NewServerBuilder apply to every response.
ts := httpxtest.NewServerBuilder(t,
httpxtest.WithHeader("X-Server", "go-snk"),
httpxtest.WithJSONContentType()).
OnRoute(http.MethodGet, "/a", http.StatusOK, myStruct{Name: "a"}).
OnRoute(http.MethodGet, "/b", http.StatusOK, myStruct{Name: "b"}).
Build()
respA, err := httpx.GetRawResponse(context.Background(), ts.URL+"/a")
if err != nil {
panic(err)
}
defer func() { _ = respA.Body.Close() }()
respB, err := httpx.GetRawResponse(context.Background(), ts.URL+"/b")
if err != nil {
panic(err)
}
defer func() { _ = respB.Body.Close() }()
fmt.Println(respA.Header.Get("X-Server"))
fmt.Println(respA.Header.Get("Content-Type"))
Output: go-snk application/json
func (*ServerBuilder) Build ¶
func (sb *ServerBuilder) Build() *httptest.Server
Build creates a new HTTP server.
func (*ServerBuilder) BuildTLS ¶
func (sb *ServerBuilder) BuildTLS() *httptest.Server
BuildTLS creates a new HTTPS server.
Example ¶
t := &testing.T{}
// BuildTLS serves over HTTPS. Clients must trust the test certificate;
// here we skip verification.
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "secure"}).
BuildTLS()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL, httpx.WithInsecureSkipVerify())
if err != nil {
panic(err)
}
fmt.Println(resp.StatusCode, resp.Result.Name)
Output: 200 secure
func (*ServerBuilder) On ¶
func (sb *ServerBuilder) On(statusCode int, response any, options ...Option) *ServerBuilder
On defines a wildcard path handler. If the response is nil, the server always responds with 204 No Content regardless of statusCode.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/SharkByteSoftware/go-snk/httpx"
"github.com/SharkByteSoftware/go-snk/httpxtest"
)
func main() {
t := &testing.T{}
// A nil response writes 204 No Content with an empty body.
ts := httpxtest.NewServerBuilder(t).
On(http.StatusCreated, nil).
Build()
resp, err := httpx.GetRawResponse(context.Background(), ts.URL)
if err != nil {
panic(err)
}
defer func() { _ = resp.Body.Close() }()
fmt.Println(resp.StatusCode)
}
Output: 204
func (*ServerBuilder) OnFunc ¶
func (sb *ServerBuilder) OnFunc(handler http.HandlerFunc, options ...Option) *ServerBuilder
OnFunc defines a wildcard handler function.
Example ¶
t := &testing.T{}
// OnFunc sets a custom handler for any unmatched request.
ts := httpxtest.NewServerBuilder(t).
OnFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(myStructReturn))
}).
Build()
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.StatusCode, resp.Result.Name)
Output: 200 test
func (*ServerBuilder) OnRoute ¶
func (sb *ServerBuilder) OnRoute(method string, route string, statusCode int, response any, options ...Option) *ServerBuilder
OnRoute defines a handler for a specific route. If the response is nil, the server always responds with 204 No Content regardless of statusCode.
Example ¶
t := &testing.T{}
// OnRoute responds to a specific method/route; anything else falls
// through to the default handler set with On.
ts := httpxtest.NewServerBuilder(t).
On(http.StatusOK, myStruct{Name: "default"}).
OnRoute(http.MethodGet, "/v1/horton", http.StatusOK, myStruct{Name: "Horton"}).
Build()
matched, err := httpx.Get[myStruct](context.Background(), ts.URL+"/v1/horton")
if err != nil {
panic(err)
}
unmatched, err := httpx.Get[myStruct](context.Background(), ts.URL+"/other")
if err != nil {
panic(err)
}
fmt.Println(matched.Result.Name)
fmt.Println(unmatched.Result.Name)
Output: Horton default
func (*ServerBuilder) OnRouteFunc ¶
func (sb *ServerBuilder) OnRouteFunc(method string, route string, handler http.HandlerFunc, options ...Option) *ServerBuilder
OnRouteFunc defines a handler for a specific route.
Example ¶
t := &testing.T{}
// OnRouteFunc registers a custom handler for a specific method/route.
ts := httpxtest.NewServerBuilder(t).
OnRouteFunc(http.MethodPost, "/v1/echo", func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
w.WriteHeader(http.StatusCreated)
_, _ = w.Write(body)
}).
Build()
resp, err := httpx.Post[myStruct](context.Background(), ts.URL+"/v1/echo", myStruct{Name: "echo"})
if err != nil {
panic(err)
}
fmt.Println(resp.StatusCode, resp.Result.Name)
Output: 201 echo
func (*ServerBuilder) OnRouteSequence ¶ added in v1.3.0
func (sb *ServerBuilder) OnRouteSequence(method, route string, exhaust ExhaustBehavior, responses ...SequencedResponse) *ServerBuilder
OnRouteSequence registers an ordered sequence of responses for a method/route.
Example ¶
t := &testing.T{}
// OnRouteSequence returns each response in order for successive requests to
// a specific method/route. Useful for simulating a resource that changes
// between polls, e.g. a job that transitions from pending to done.
ts := httpxtest.NewServerBuilder(t).
OnRouteSequence(http.MethodGet, "/v1/job", httpxtest.ExhaustRepeatLast,
httpxtest.Response(http.StatusOK, myStruct{Name: "pending"}),
httpxtest.Response(http.StatusOK, myStruct{Name: "done"}),
).
Build()
for range 3 {
resp, err := httpx.Get[myStruct](context.Background(), ts.URL+"/v1/job")
if err != nil {
panic(err)
}
fmt.Println(resp.Result.Name)
}
Output: pending done done
func (*ServerBuilder) OnSequence ¶ added in v1.3.0
func (sb *ServerBuilder) OnSequence(exhaust ExhaustBehavior, responses ...SequencedResponse) *ServerBuilder
OnSequence registers an ordered sequence of responses for the default handler.
Example ¶
t := &testing.T{}
// OnSequence returns each response in order for successive requests to the
// default handler. ExhaustRepeatLast keeps returning the final entry once
// the sequence is exhausted.
ts := httpxtest.NewServerBuilder(t).
OnSequence(httpxtest.ExhaustRepeatLast,
httpxtest.Response(http.StatusOK, myStruct{Name: "first"}),
httpxtest.Response(http.StatusOK, myStruct{Name: "second"}),
).
Build()
for range 3 {
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.Result.Name)
}
Output: first second second
Example (ExhaustCycle) ¶
t := &testing.T{}
// ExhaustCycle wraps back to the first entry after the last one is served.
ts := httpxtest.NewServerBuilder(t).
OnSequence(httpxtest.ExhaustCycle,
httpxtest.Response(http.StatusOK, myStruct{Name: "a"}),
httpxtest.Response(http.StatusOK, myStruct{Name: "b"}),
).
Build()
for range 4 {
resp, err := httpx.Get[myStruct](context.Background(), ts.URL)
if err != nil {
panic(err)
}
fmt.Println(resp.Result.Name)
}
Output: a b a b