httpex

package
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Jan 15, 2020 License: MIT Imports: 18 Imported by: 1

Documentation

Overview

Package http helps with end-to-end HTTP and REST API testing.

Usage examples

See example directory:

Communication mode

There are two common ways to test API with http:

  • start HTTP server and instruct http to use HTTP client for communication
  • don't start server and instruct http to invoke http handler directly

The second approach works only if the server is a Go module and its handler can be imported in tests.

Concrete behaviour is determined by Client implementation passed to Config struct. If you're using http.Client, set its Transport field (http.RoundTriper) to one of the following:

  1. default (nil) - use HTTP transport from net/http (you should start server)
  2. http.Binder - invoke given http.Handler directly
  3. http.FastBinder - invoke given fasthttp.RequestHandler directly

Note that http handler can be usually obtained from http framework you're using. E.g., echo framework provides either http.Handler or fasthttp.RequestHandler.

You can also provide your own implementation of RequestFactory (creates http.Request), or Client (gets http.Request and returns http.response).

If you're starting server from tests, it's very handy to use net/http/httptest.

Value equality

Whenever values are checked for equality in http, they are converted to "canonical form":

  • structs are converted to map[string]interface{}
  • type aliases are removed
  • numeric types are converted to float64
  • non-nil interfaces pointing to nil slices and maps are replaced with nil interfaces

This is equivalent to subsequently json.Marshal() and json.Unmarshal() the value and currently is implemented so.

Failure handling

When some check fails, failure is reported. If non-fatal failures are used (see Reporter interface), execution is continued and instance that was checked is marked as failed.

If specific instance is marked as failed, all subsequent checks are ignored for this instance and for any child instances retrieved after failure.

Example:

array := NewArray(NewAssertReporter(t), []interface{}{"foo", 123})

e0 := array.Element(0)  // success
e1 := array.Element(1)  // success

s0 := e0.String()  // success
s1 := e1.String()  // failure; e1 and s1 are marked as failed, e0 and s0 are not

s0.Equal("foo")    // success
s1.Equal("bar")    // this check is ignored because s1 is marked as failed

Index

Constants

View Source
const (
	MIMEJSON              = "application/json"
	MIMEHTML              = "text/html"
	MIMEXML               = "application/xml"
	MIMEXML2              = "text/xml"
	MIMEPlain             = "text/plain"
	MIMEPOSTForm          = "application/x-www-form-urlencoded"
	MIMEMultipartPOSTForm = "multipart/form-data"
	MIMEPROTOBUF          = "application/x-protobuf"
	MIMEMSGPACK           = "application/x-msgpack"
	MIMEMSGPACK2          = "application/msgpack"
	MIMEYAML              = "application/x-yaml"
)

Variables

This section is empty.

Functions

func New

func New(baseURL string) *expect

New returns a new expect object.

baseURL specifies URL to prepended to all Request. My be empty. If non-empty, trailing slash is allowed but not required and is appended automatically.

New is a shorthand for WithConfig. It uses:

  • CompactPrinter as Printer, with testing.TB as Logger
  • AssertReporter as Reporter
  • defaultRequestFactory as RequestFactory

Client is set to a default client with a non-nil Jar:

&http.Client{
    Jar: http.NewJar(),
}

Example:

func TestSomething(t *testing.T) {
    e := http.New(t, "http://example.com/")

    e.GET("/path").
        expect().
        Status(http.StatusOK)
}

func WithConfig

func WithConfig(config Config) *expect

WithConfig returns a new expect object with given config.

If RequestFactory is nil, it's set to a defaultRequestFactory instance.

If Client is nil, it's set to a default client with a non-nil Jar:

&http.Client{
    Jar: http.NewJar(),
}

Example:

func TestWithConfig(t *testing.T) {
    e := http.WithConfig(http.Config{
        BaseURL:  "http://example.com/",
        Client:   &http.Client{
            Transport: http.NewBinder(myHandler()),
            Jar:       http.NewJar(),
        },
    })

    e.GET("/path").
        expect().
        Status(http.StatusOK)
}

Types

type Client

type Client interface {
	// Do sends Request and returns response.
	Do(*http.Request) (*http.Response, error)
}

Client is used to send http.Request and receive http.response. http.Client implements this interface.

Binder and FastBinder may be used to obtain this interface implementation.

Example:

httpBinderClient := &http.Client{
  Transport: http.NewBinder(HTTPHandler),
}
fastBinderClient := &http.Client{
  Transport: http.NewFastBinder(FastHTTPHandler),
}

type Config

type Config struct {
	// BaseURL is a URL to prepended to all Request. My be empty. If
	// non-empty, trailing slash is allowed but not required and is
	// appended automatically.
	BaseURL string

	// RequestFactory is used to pass in a custom *http.Request generation func.
	// May be nil.
	//
	// You can use defaultRequestFactory, or provide custom implementation.
	// Useful for Google App Engine testing for example.
	RequestFactory RequestFactory

	// Client is used to send http.Request and receive http.response.
	// Should not be nil.
	//
	// You can use http.DefaultClient or http.Client, or provide
	// custom implementation.
	Client Client
}

Config contains various settings.

type Request

type Request struct {
	// contains filtered or unexported fields
}

Request provides methods to incrementally build http.Request object, send it, and receive response.

func (*Request) Expect

func (r *Request) Expect() *response

expect constructs http.Request, sends it, receives http.response, and returns a new response object to inspect received response.

Request is sent using Config.Client interface, or Config.Dialer interface in case of WebSocket Request.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithJSON(map[string]interface{}{"foo": 123})
resp := req.expect()
resp.Status(http.StatusOK)

func (*Request) LastError

func (r *Request) LastError() error

func (*Request) WithAccept

func (r *Request) WithAccept(accept string) *Request

func (*Request) WithAcceptEncoding

func (r *Request) WithAcceptEncoding(acceptEncoding string) *Request

func (*Request) WithAcceptLanguage

func (r *Request) WithAcceptLanguage(acceptLanguage string) *Request

func (*Request) WithAuthorization

func (r *Request) WithAuthorization(authorization string) *Request

func (*Request) WithBasicAuth

func (r *Request) WithBasicAuth(username, password string) *Request

WithBasicAuth sets the Request's Authorization header to use HTTP Basic Authentication with the provided username and password.

With HTTP Basic Authentication the provided username and password are not encrypted.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithBasicAuth("john", "secret")

func (*Request) WithBearerToken

func (r *Request) WithBearerToken(token string) *Request

func (*Request) WithBytes

func (r *Request) WithBytes(b []byte) *Request

WithBytes sets Request body to given slice of bytes.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithHeader("Content-Type": "application/json")
req.WithBytes([]byte(`{"foo": 123}`))

func (*Request) WithChunked

func (r *Request) WithChunked(reader io.Reader) *Request

WithChunked enables chunked encoding and sets Request body reader.

expect() will read all available data from given reader. Content-Length is not set, and "chunked" Transfer-Encoding is used.

If protocol version is not at least HTTP/1.1 (required for chunked encoding), failure is reported.

Example:

req := NewRequest(config, "PUT", "http://example.com/upload")
fh, _ := os.Open("data")
defer fh.Close()
req.WithHeader("Content-Type": "application/octet-stream")
req.WithChunked(fh)

func (*Request) WithClient

func (r *Request) WithClient(client Client) *Request

WithClient sets client.

The new client overwrites Config.Client. It will be used once to send the Request and receive a response.

Example:

req := NewRequest(config, "GET", "/path")
req.WithClient(&http.Client{
  Transport: &http.Transport{
    DisableCompression: true,
  },
})

func (*Request) WithConnection

func (r *Request) WithConnection(connection string) *Request

func (*Request) WithContentType

func (r *Request) WithContentType(contentType string) *Request

func (*Request) WithCookie

func (r *Request) WithCookie(k, v string) *Request

WithCookie adds given single cookie to Request.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithCookie("name", "value")

func (*Request) WithCookies

func (r *Request) WithCookies(cookies map[string]string) *Request

WithCookies adds given cookies to Request.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithCookies(map[string]string{
    "foo": "aa",
    "bar": "bb",
})

func (*Request) WithFile

func (r *Request) WithFile(key, path string, reader ...io.Reader) *Request

WithFile sets Content-Type header to "multipart/form-data", reads given file and adds its contents to Request body.

If reader is given, it's used to read file contents. Otherwise, os.Open() is used to read a file with given path.

Multiple WithForm(), WithFormField(), and WithFile() calls may be combined. WithMultipart() should be called before WithFile(), otherwise WithFile() fails.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithFile("avatar", "./john.png")

req := NewRequest(config, "PUT", "http://example.com/path")
fh, _ := os.Open("./john.png")
req.WithMultipart().
    WithFile("avatar", "john.png", fh)
fh.Close()

func (*Request) WithFileBytes

func (r *Request) WithFileBytes(key, path string, data []byte) *Request

WithFileBytes is like WithFile, but uses given slice of bytes as the file contents.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
fh, _ := os.Open("./john.png")
b, _ := ioutil.ReadAll(fh)
req.WithMultipart().
    WithFileBytes("avatar", "john.png", b)
fh.Close()

func (*Request) WithForm

func (r *Request) WithForm(object interface{}) *Request

WithForm sets Content-Type header to "application/x-www-form-urlencoded" or (if WithMultipart() was called) "multipart/form-data", converts given object to url.Values using github.com/ajg/form, and adds it to Request body.

Various object types are supported, including maps and structs. Structs may contain "form" struct tag, similar to "json" struct tag for json.Marshal(). See https://github.com/ajg/form for details.

Multiple WithForm(), WithFormField(), and WithFile() calls may be combined. If WithMultipart() is called, it should be called first.

Example:

type MyForm struct {
    Foo int `form:"foo"`
}

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithForm(MyForm{Foo: 123})

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithForm(map[string]interface{}{"foo": 123})

func (*Request) WithFormField

func (r *Request) WithFormField(key string, value interface{}) *Request

WithFormField sets Content-Type header to "application/x-www-form-urlencoded" or (if WithMultipart() was called) "multipart/form-data", converts given value to string using fmt.Sprint(), and adds it to Request body.

Multiple WithForm(), WithFormField(), and WithFile() calls may be combined. If WithMultipart() is called, it should be called first.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithFormField("foo", 123).
    WithFormField("bar", 456)

func (*Request) WithFunc

func (r *Request) WithFunc(f func(req *Request) *Request) *Request

WithFunc can set any value with customer func

func (*Request) WithHeader

func (r *Request) WithHeader(k, v string) *Request

WithHeader adds given single header to Request.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithHeader("Content-Type": "application/json")

func (*Request) WithHeaders

func (r *Request) WithHeaders(headers map[string]string) *Request

WithHeaders adds given headers to Request.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithHeaders(map[string]string{
    "Content-Type": "application/json",
})

func (*Request) WithHttpCookie

func (r *Request) WithHttpCookie(cookies ...*http.Cookie) *Request

func (*Request) WithJSON

func (r *Request) WithJSON(object interface{}) *Request

WithJSON sets Content-Type header to "application/json; charset=utf-8" and sets body to object, marshaled using json.Marshal().

Example:

type MyJSON struct {
    Foo int `json:"foo"`
}

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithJSON(MyJSON{Foo: 123})

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithJSON(map[string]interface{}{"foo": 123})

func (*Request) WithKeepAlive

func (r *Request) WithKeepAlive() *Request

func (*Request) WithMultiHeaders

func (r *Request) WithMultiHeaders(headers map[string][]string) *Request

func (*Request) WithMultipart

func (r *Request) WithMultipart() *Request

WithMultipart sets Content-Type header to "multipart/form-data".

After this call, WithForm() and WithFormField() switch to multipart form instead of urlencoded form.

If WithMultipart() is called, it should be called before WithForm(), WithFormField(), and WithFile().

WithFile() always requires WithMultipart() to be called first.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithMultipart().
    WithForm(map[string]interface{}{"foo": 123})

func (*Request) WithPath

func (r *Request) WithPath(key string, value interface{}) *Request

WithPath substitutes named parameters in url path.

value is converted to string using fmt.Sprint(). If there is no named parameter '{key}' in url path, failure is reported.

Named parameters are case-insensitive.

Example:

req := NewRequest(config, "POST", "/repos/{user}/{repo}")
req.WithPath("user", "gavv")
req.WithPath("repo", "http")
// path will be "/repos/gavv/http"

func (*Request) WithProto

func (r *Request) WithProto(proto string) *Request

WithProto sets HTTP protocol version.

proto should have form of "HTTP/{major}.{minor}", e.g. "HTTP/1.1".

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithProto("HTTP/2.0")

func (*Request) WithQuery

func (r *Request) WithQuery(key string, value interface{}) *Request

WithQuery adds query parameter to Request URL.

value is converted to string using fmt.Sprint() and urlencoded.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithQuery("a", 123)
req.WithQuery("b", "foo")
// URL is now http://example.com/path?a=123&b=foo

func (*Request) WithQueryString

func (r *Request) WithQueryString(query string) *Request

WithQueryString parses given query string and adds it to Request URL.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithQuery("a", 11)
req.WithQueryString("b=22&c=33")
// URL is now http://example.com/path?a=11&bb=22&c=33

func (*Request) WithReferer

func (r *Request) WithReferer(ref string) *Request

func (*Request) WithText

func (r *Request) WithText(s string) *Request

WithText sets Content-Type header to "text/plain; charset=utf-8" and sets body to given string.

Example:

req := NewRequest(config, "PUT", "http://example.com/path")
req.WithText("hello, world!")

func (*Request) WithURL

func (r *Request) WithURL(urlStr string) *Request

WithURL sets Request URL.

This URL overwrites Config.BaseURL. Request path passed to NewRequest() is appended to this URL, separated by slash if necessary.

Example:

req := NewRequest(config, "PUT", "/path")
req.WithURL("http://example.com")
// URL is now http://example.com/path

func (*Request) WithUserAgent

func (r *Request) WithUserAgent(userAgent string) *Request

func (*Request) WithXMLHttpRequest

func (r *Request) WithXMLHttpRequest() *Request

func (*Request) WithXRequestedWith

func (r *Request) WithXRequestedWith(requestedWith string) *Request

type RequestFactory

type RequestFactory interface {
	NewRequest(method, urlStr string, body io.Reader) (*http.Request, error)
}

RequestFactory is used to create all http.Request objects.

func DefaultRequestFactory

func DefaultRequestFactory() RequestFactory

DefaultRequestFactory return a defaultRequestFactory Instance.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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