Documentation
¶
Overview ¶
Package httpregistry provides multiple utilities that can be used to simplify the creation of /net/http/httptest mock servers. That package allows the creation of http servers that can be used to respond to actual http calls in tests. This package aims at providing a nicer interface that should cover the most standard cases and attempts to hide away a layer of boilerplate. For example it is normal to write test code like this
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet && r.URL.Path == "/users" {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusInternalServerError)
}))
with this package this can be simplified to
registry := NewRegistry(t)
registry.AddMethodAndURL("/users", http.MethodGet)
ts := registry.GetServer()
Similarly this package tries to help with the harder task to test if a POST/PUT request actually happen to have the expected body/parameters. With this library this can be done as
registry := NewRegistry(t)
registry.AddRequest(
httpregistry.Request().
WithURL("/users").
WithMethod(http.MethodPost).
WithJSONHeader().
WithBody([]byte("{\"user\": \"John Schmidt\"}"))
ts := registry.GetServer()
For more examples of what this package is capable of, refer to the README file.
Index ¶
- Variables
- type CustomResponse
- type MockTestingT
- type Registry
- func (reg *Registry) Add()
- func (reg *Registry) AddBody(body []byte)
- func (reg *Registry) AddInfiniteResponse(response mockResponse)
- func (reg *Registry) AddMethod(method string)
- func (reg *Registry) AddMethodAndURL(method string, URL string)
- func (reg *Registry) AddMethodAndURLWithStatusCode(method string, URL string, statusCode int)
- func (reg *Registry) AddMethodWithStatusCode(method string, statusCode int)
- func (reg *Registry) AddRequest(request Request)
- func (reg *Registry) AddRequestWithInfiniteResponse(request Request, response mockResponse)
- func (reg *Registry) AddRequestWithResponse(request Request, response mockResponse)
- func (reg *Registry) AddRequestWithResponses(request Request, responses ...mockResponse)
- func (reg *Registry) AddResponse(response mockResponse)
- func (reg *Registry) AddResponses(responses ...mockResponse)
- func (reg *Registry) AddURL(URL string)
- func (reg *Registry) AddURLWithStatusCode(URL string, statusCode int)
- func (reg *Registry) CheckAllResponsesAreConsumed()
- func (reg *Registry) GetMatchesForRequest(r Request) []*http.Request
- func (reg *Registry) GetMatchesForURL(url string) []*http.Request
- func (reg *Registry) GetMatchesURLAndMethod(url string, method string) []*http.Request
- func (reg *Registry) GetServer() *httptest.Server
- func (reg *Registry) Why() string
- type Request
- func (r Request) Equal(r2 Request) bool
- func (r Request) String() string
- func (r Request) WithBody(body []byte) Request
- func (r Request) WithHeader(header string, value string) Request
- func (r Request) WithHeaders(headers map[string]string) Request
- func (r Request) WithJSONBody(body any) Request
- func (r Request) WithJSONHeader() Request
- func (r Request) WithMethod(method string) Request
- func (r Request) WithName(name string) Request
- func (r Request) WithStringBody(body string) Request
- func (r Request) WithURL(URL string) Request
- type Response
- func (res Response) String() string
- func (res Response) WithBody(body []byte) Response
- func (res Response) WithHeader(header string, value string) Response
- func (res Response) WithHeaders(headers map[string]string) Response
- func (res Response) WithJSONBody(body any) Response
- func (res Response) WithJSONHeader() Response
- func (res Response) WithName(name string) Response
- func (res Response) WithStatus(statusCode int) Response
- type TestingT
Constants ¶
This section is empty.
Variables ¶
var ( ContinueResponse = newResponseWithName("httpregistry.ContinueResponse").WithStatus(100) SwitchingProtocolsResponse = newResponseWithName("httpregistry.SwitchingProtocolsResponse").WithStatus(101) ProcessingResponse = newResponseWithName("httpregistry.ProcessingResponse").WithStatus(102) EarlyHintsResponse = newResponseWithName("httpregistry.EarlyHintsResponse").WithStatus(103) )
Information responses
var ( OkResponse = newResponseWithName("httpregistry.OkResponse").WithStatus(200) CreatedResponse = newResponseWithName("httpregistry.CreatedResponse").WithStatus(201) AcceptedResponse = newResponseWithName("httpregistry.AcceptedResponse").WithStatus(202) NonAuthoritativeInformationResponse = newResponseWithName("httpregistry.NonAuthoritativeInformationResponse").WithStatus(203) NoContentResponse = newResponseWithName("httpregistry.NoContentResponse").WithStatus(204) ResetContentResponse = newResponseWithName("httpregistry.ResetContentResponse").WithStatus(205) PartialContentResponse = newResponseWithName("httpregistry.PartialContentResponse").WithStatus(206) MultiStatusResponse = newResponseWithName("httpregistry.MultiStatusResponse").WithStatus(207) AlreadyReportedResponse = newResponseWithName("httpregistry.AlreadyReportedResponse").WithStatus(208) )
Successful responses
var ( MultipleChoicesResponse = newResponseWithName("httpregistry.MultipleChoicesResponse").WithStatus(300) MovedPermanentlyResponse = newResponseWithName("httpregistry.MovedPermanentlyResponse").WithStatus(301) FoundResponse = newResponseWithName("httpregistry.FoundResponse").WithStatus(302) SeeOtherResponse = newResponseWithName("httpregistry.SeeOtherResponse").WithStatus(303) NotModifiedResponse = newResponseWithName("httpregistry.NotModifiedResponse").WithStatus(304) TemporaryRedirectResponse = newResponseWithName("httpregistry.TemporaryRedirectResponse").WithStatus(307) PermanentRedirectResponse = newResponseWithName("httpregistry.PermanentRedirectResponse").WithStatus(308) )
Redirection messages
var ( BadRequestsResponse = newResponseWithName("httpregistry.BadRequestsResponse").WithStatus(400) PaymentRequiredResponse = newResponseWithName("httpregistry.PaymentRequiredResponse").WithStatus(402) ForbiddenResponse = newResponseWithName("httpregistry.ForbiddenResponse").WithStatus(403) NotFoundResponse = newResponseWithName("httpregistry.NotFoundResponse").WithStatus(404) MethodNotAllowedResponse = newResponseWithName("httpregistry.MethodNotAllowedResponse").WithStatus(405) NotAcceptableResponse = newResponseWithName("httpregistry.NotAcceptableResponse").WithStatus(406) ProxyAuthenticationRequiredResponse = newResponseWithName("httpregistry.ProxyAuthenticationRequiredResponse").WithStatus(407) RequestTimeoutResponse = newResponseWithName("httpregistry.RequestTimeoutResponse").WithStatus(408) ConflictResponse = newResponseWithName("httpregistry.ConflictResponse").WithStatus(409) GoneResponse = newResponseWithName("httpregistry.GoneResponse").WithStatus(410) LengthRequiredResponse = newResponseWithName("httpregistry.LengthRequiredResponse").WithStatus(411) PreconditionFailedResponse = newResponseWithName("httpregistry.PreconditionFailedResponse").WithStatus(412) PayloadTooLargeResponse = newResponseWithName("httpregistry.PayloadTooLargeResponse").WithStatus(413) URITooLongResponse = newResponseWithName("httpregistry.URITooLongResponse").WithStatus(414) UnsupportedMediaTypeResponse = newResponseWithName("httpregistry.UnsupportedMediaTypeResponse").WithStatus(415) RangeNotSatisfiableResponse = newResponseWithName("httpregistry.RangeNotSatisfiableResponse").WithStatus(416) ExpectationFailedResponse = newResponseWithName("httpregistry.ExpectationFailedResponse").WithStatus(417) IAmATeapotResponse = newResponseWithName("httpregistry.IAmATeapotResponse").WithStatus(418) MisdirectedRequestResponse = newResponseWithName("httpregistry.MisdirectedRequestResponse").WithStatus(421) UpgradeRequiredResponse = newResponseWithName("httpregistry.UpgradeRequiredResponse").WithStatus(426) ReconditionRequiredResponse = newResponseWithName("httpregistry.ReconditionRequiredResponse").WithStatus(428) RequestHeaderFieldsTooLargeResponse = newResponseWithName("httpregistry.RequestHeaderFieldsTooLargeResponse").WithStatus(431) )
Client error responses
var ( InternalServerErrorResponse = newResponseWithName("httpregistry.InternalServerErrorResponse").WithStatus(500) NotImplementedResponse = newResponseWithName("httpregistry.NotImplementedResponse").WithStatus(501) BadGatewayResponse = newResponseWithName("httpregistry.BadGatewayResponse").WithStatus(502) GatewayTimeoutResponse = newResponseWithName("httpregistry.GatewayTimeoutResponse").WithStatus(504) HTTPVersionNotSupportedResponse = newResponseWithName("httpregistry.HTTPVersionNotSupportedResponse").WithStatus(505) VariantAlsoNegotiatesResponse = newResponseWithName("httpregistry.VariantAlsoNegotiatesResponse").WithStatus(506) NotExtendedResponse = newResponseWithName("httpregistry.NotExtendedResponse").WithStatus(510) NetworkAuthenticationResponse = newResponseWithName("httpregistry.NetworkAuthenticationResponse").WithStatus(511) )
Server error responses
var DefaultRequest = newRequestWithName("httpregistry.DefaultRequest")
DefaultRequest represents the request that is used when no request is specified. It will match any request. This is useful in combination with registry.GetMatchesForRequest so that it is possible to retrieve all the matches associated with it
Functions ¶
This section is empty.
Types ¶
type CustomResponse ¶
type CustomResponse struct {
// contains filtered or unexported fields
}
CustomResponse allows the user to define a custom made response to any request. In particular it allows to define responses that are functions of the request
for example
func(w http.ResponseWriter, r *http.Request) {
regexUser := regexp.MustCompile(`/users/(?P<userID>.+)/address$`)
if regexUser.MatchString(r.URL.Path) {
matches := regexUser.FindStringSubmatch(r.URL.Path)
userID := matches[regexUser.SubexpIndex("userID")]
body := map[string]string{"user_id": userID}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(&body)
return
}
}
func NewCustomResponse ¶
func NewCustomResponse(f func(w http.ResponseWriter, r *http.Request)) CustomResponse
NewCustomResponse creates a new FunctionalResponse. A FunctionalResponse allows the user to define a custom made response to any request. In particular it allows to define responses that are functions of the request
for example
func(w http.ResponseWriter, r *http.Request) {
regexUser := regexp.MustCompile(`/users/(?P<userID>.+)/address$`)
if regexUser.MatchString(r.URL.Path) {
matches := regexUser.FindStringSubmatch(r.URL.Path)
userID := matches[regexUser.SubexpIndex("userID")]
body := map[string]string{"user_id": userID}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(&body)
return
}
}
func (CustomResponse) String ¶
func (res CustomResponse) String() string
String marshal CustomResponse to string
func (CustomResponse) WithName ¶
func (res CustomResponse) WithName(name string) CustomResponse
WithName allows to add a name to a FunctionalResponse so that it can be better identified when debugging. By the fault FunctionalResponse gets a sequential name that can be hard to identify if there are many of them
type MockTestingT ¶
MockTestingT mocks the testing.T interface and it can be used to assert that test that should fail will fail
func NewMockTestingT ¶
func NewMockTestingT() *MockTestingT
NewMockTestingT returns a MockTestingT that can be passed as argument of httpregistry.NewRegistry so that is possible to make assertions on the state of the test or on the message that it returns
func (*MockTestingT) Errorf ¶
func (f *MockTestingT) Errorf(format string, args ...any)
Errorf records what error message was emitted
func (*MockTestingT) Fail ¶
func (f *MockTestingT) Fail()
Fail records that the Fail function was called
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry represents a collection of matches that associate to a http request a http response. It contains all the Match that were registered and after the server is called it contains all the reasons why a request did not match with a particular match the testing.T is used to signal that there was an unexpected error or that not all the responses were consumed as expected
func (*Registry) Add ¶
func (reg *Registry) Add()
Add adds to the registry a 200 response for any requests
reg := httpregistry.NewRegistry(t) reg.Add() reg.GetServer()
will create a http server that returns 200 on calling anything.
func (*Registry) AddBody ¶
AddBody adds to the registry a statusCode response for a request that matches method and URL
reg := httpregistry.NewRegistry(t) reg.AddSimpleRequest(PUT, "/foo", 204) reg.GetServer()
will create a http server that returns 204 on calling GET "/foo" and fails the test on anything else
func (*Registry) AddInfiniteResponse ¶
func (reg *Registry) AddInfiniteResponse(response mockResponse)
AddInfiniteResponse adds to the registry a generic response that is returned for any call and it is never consumed
reg := httpregistry.NewRegistry(t)
reg.AddInfiniteResponse(
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
)
reg.GetServer()
will create a http server that returns 204 with "hello" as body on calling the server on any URL for as many times as needed
func (*Registry) AddMethod ¶
AddMethod adds to the registry a 200 response for a request that matches the method
reg := httpregistry.NewRegistry(t)
reg.AddMethod("/foo")
reg.GetServer()
will create a http server that returns 200 on calling GET "/foo" and fails the test on anything else
func (*Registry) AddMethodAndURL ¶
AddMethodAndURL adds to the registry a 200 response for a request that matches method and URL
reg := httpregistry.NewRegistry(t) reg.AddMethodAndURL(GET, "/foo") reg.GetServer()
will create a http server that returns 200 on calling GET "/foo" and fails the test on anything else
func (*Registry) AddMethodAndURLWithStatusCode ¶
AddMethodAndURLWithStatusCode adds to the registry a statusCode response for a request that matches method and URL
reg := httpregistry.NewRegistry(t) reg.AddSimpleRequest(PUT, "/foo", 204) reg.GetServer()
will create a http server that returns 204 on calling GET "/foo" and fails the test on anything else
func (*Registry) AddMethodWithStatusCode ¶
AddMethodWithStatusCode adds to the registry a statusCode response for a request that matches the method
reg := httpregistry.NewRegistry(t)
reg.AddMethodWithStatusCode("/foo", 401)
reg.GetServer()
will create a http server that returns 401 on calling GET "/foo" and fails the test on anything else
func (*Registry) AddRequest ¶
AddRequest adds to the registry a 200 response for a generic request that needs to be matched
reg := httpregistry.NewRegistry(t)
reg.AddRequest(
httpregistry.NewRequest(GET, "/foo", httpregistry.WithRequestHeader("header", "value"))
)
reg.GetServer()
will create a http server that returns 200 on calling GET "/foo" with the correct header and fails the test on anything else
func (*Registry) AddRequestWithInfiniteResponse ¶
AddRequestWithInfiniteResponse adds to the registry a generic response for a generic request that needs to be matched
reg := httpregistry.NewRegistry(t)
reg.AddRequestWithInfiniteResponse(
httpregistry.NewRequest(GET, "/foo", httpregistry.WithRequestHeader("header", "value")),
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
)
reg.GetServer()
will create a http server that returns 204 with "hello" as body on calling GET "/foo" with the correct header and fails the test on anything else
func (*Registry) AddRequestWithResponse ¶
AddRequestWithResponse adds to the registry a generic response for a generic request that needs to be matched
reg := httpregistry.NewRegistry(t)
reg.AddRequest(
httpregistry.NewRequest(GET, "/foo", httpregistry.WithRequestHeader("header", "value")),
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
)
reg.GetServer()
will create a http server that returns 204 with "hello" as body on calling GET "/foo" with the correct header and fails the test on anything else
func (*Registry) AddRequestWithResponses ¶
AddRequestWithResponses adds to the registry multiple responses for a generic request that needs to be matched. The responses are consumed by the calls so if more calls than responses will happen then the test will fail
reg := httpregistry.NewRegistry(t)
reg.AddRequestWithResponses(
httpregistry.NewRequest(GET, "/foo", httpregistry.WithRequestHeader("header", "value")),
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
httpregistry.NewResponse(http.Ok, []byte{"hello again"}),
)
reg.GetServer()
will create a http server that returns 204 with "hello" as body on calling GET "/foo" the first call with the correct header, it returns 200 with "hello again" as body on the second call with the correct header and fails the test on anything else
func (*Registry) AddResponse ¶
func (reg *Registry) AddResponse(response mockResponse)
AddResponse adds to the registry a generic response that is returned for any call
reg := httpregistry.NewRegistry(t)
reg.AddResponse(
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
)
reg.GetServer()
will create a http server that returns 204 with "hello" as body on calling the server on any URL
func (*Registry) AddResponses ¶
func (reg *Registry) AddResponses(responses ...mockResponse)
AddResponses adds to the registry a generic response that is returned for any call
reg := httpregistry.NewRegistry(t)
reg.AddResponses(
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
httpregistry.NewResponse(http.StatusCreated, []byte{"hello"}),
)
reg.GetServer()
will create a http server that returns 204 with "hello" as body on calling the server on any URL for two times and then returns an error
func (*Registry) AddURL ¶
AddURL adds to the registry a 200 response for a request that matches the URL
reg := httpregistry.NewRegistry(t)
reg.AddURL("/foo")
reg.GetServer()
will create a http server that returns 200 on calling GET "/foo" and fails the test on anything else
func (*Registry) AddURLWithStatusCode ¶
AddURLWithStatusCode adds to the registry a statusCode response for a request that matches the URL
reg := httpregistry.NewRegistry(t)
reg.AddURLWithStatusCode("/foo", 401)
reg.GetServer()
will create a http server that returns 401 on calling GET "/foo" and fails the test on anything else
func (*Registry) CheckAllResponsesAreConsumed ¶
func (reg *Registry) CheckAllResponsesAreConsumed()
CheckAllResponsesAreConsumed fails the test if there are unused responses at the end of the test. This is useful to check if all the expected calls happened or if there is an unexpected behavior happening.
**Important**: If you are using AddInfiniteRequest this call will ALWAYS fail!
func (*Registry) GetMatchesForRequest ¶
GetMatchesForRequest returns the *http.Request that matched a generic Request
func (*Registry) GetMatchesForURL ¶
GetMatchesForURL returns the http.Requests that matched a specific URL independently of the method used to call it
func (*Registry) GetMatchesURLAndMethod ¶
GetMatchesURLAndMethod returns the http.Requests that matched a specific method, URL pair
func (*Registry) GetServer ¶
GetServer returns a httptest.Server designed to match all the requests registered with the Registry
func (*Registry) Why ¶
Why returns a string that contains all the reasons why the request submitted to the registry failed to match with the registered requests. The envision use of this function is just as a helper when debugging the tests, most of the time it might not be obvious if there is a typo or a small error.
type Request ¶
type Request struct {
// contains filtered or unexported fields
}
Request represents a request that will be registered to a Registry to get matched against an incoming HTTP request. The match happens against the method, the headers and the URL interpreted as a regex
func NewRequest ¶
func NewRequest() Request
NewRequest creates a new request designed to be registered to a Registry to get matched against an incoming HTTP request. This function is designed to be used in conjunction with other other receivers. For example
NewRequest().
WithURL("/users/1").
WithMethod(http.MethodPatch).
WithJSONHeader().
WithBody([]byte("{\"user\": \"John Schmidt\"}"))
func (Request) WithHeader ¶
WithHeader returns a new request with the header header set to value
func (Request) WithHeaders ¶
WithHeaders returns a new request with all the headers in headers applied. If multiple headers with the same name are defined only the last one is applied.
func (Request) WithJSONBody ¶
WithJSONBody returns a new request with the method body set to the JSON encoded version of body and the Content-Type header set to "application/json". This method panics if body cannot be converted to JSON
func (Request) WithJSONHeader ¶
WithJSONHeader returns a new request with the header `Content-Type` set to `application/json`
func (Request) WithMethod ¶
WithMethod returns a new request with the method attribute set to method
func (Request) WithName ¶
WithName allows to add a name to a Request so that it can be better identified when debugging. By the default Request gets a sequential name that can be hard to identify if there are many of them. So if clarity is needed we recommend to change the default name.
func (Request) WithStringBody ¶
WithStringBody returns a new request with the method body set to body
type Response ¶
type Response struct {
// contains filtered or unexported fields
}
Response represents a response that we want to return if the registry finds a request that matches the incoming request. If the match happens then we will return a http response that matches the attributes defined in this struct.
func NewResponse ¶
func NewResponse() Response
NewResponse creates a new Response. This function is designed to be used in conjunction with other other receivers. For example
NewResponse().
WithStatus(http.StatusOK).
WithJSONHeader().
WithBody([]byte("{\"user\": \"John Schmidt\"}"))
The default response is a 200 without any body nor header
func (Response) WithHeader ¶
WithHeader returns a new response with the header header set to value
func (Response) WithHeaders ¶
WithHeaders returns a new response with all the headers in headers applied. If multiple headers with the same name are defined only the last one is applied.
func (Response) WithJSONBody ¶
WithJSONBody returns a new response that will return body as body and will have the header `Content-Type` set to `application/json`. This method panics if body cannot be converted to JSON
func (Response) WithJSONHeader ¶
WithJSONHeader returns a new Response with the header `Content-Type` set to `application/json`
func (Response) WithName ¶
WithName allows to add a name to a Response so that it can be better identified when debugging. By the default Response gets a sequential name that can be hard to identify if there are many of them. So if clarity is needed we recommend to change the default name.
func (Response) WithStatus ¶
WithStatus returns a new response with the StatusCode attribute set to statusCode
type TestingT ¶
TestingT is the subset of testing.T (see also testing.TB) used by the httpregistry package. The reason why this exists is so that we can mock in test and check if failures happen when we expect. See the readme or the tests for an example of how to use this. By design testing.TB make it impossible for the end user to implement the interface so this is the only way to do so