httpexpect alternatives and similar packages
Based on the "Testing" category.
Alternatively, view httpexpect alternatives based on common mentions on social networks and blogs.
-
realize
Realize is the #1 Golang Task Runner which enhance your workflow by automating the most common tasks and using the best performing Golang live reloading. -
Testcontainers-go
Testcontainers for Go is a Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done. -
dockertest
Write better integration tests! Dockertest helps you boot up ephermal docker images for your Go tests with minimal work. -
selenoid
Selenium Hub successor running browsers within containers. Scalable, immutable, self hosted Selenium-Grid on any platform with single binary. -
hoverfly
Lightweight service virtualization/ API simulation / API mocking tool for developers and testers -
playwright-go
Playwright for Go a browser automation library to control Chromium, Firefox and WebKit with a single API. -
gnomock
Test your code without writing mocks with ephemeral Docker containers 📦 Setup popular services with just a couple lines of code ⏱️ No bash, no yaml, only code 💻 -
embedded-postgres
Run a real Postgres database locally on Linux, OSX or Windows as part of another Go application or test -
cdp
Package cdp provides type-safe bindings for the Chrome DevTools Protocol (CDP), written in the Go programming language. -
gotest.tools
A collection of packages to augment the go testing package and support common patterns. -
go-testdeep
Extremely flexible golang deep comparison, extends the go testing package, tests HTTP APIs and provides tests suite -
testza
Full-featured test framework for Go! Assertions, fuzzing, input testing, output capturing, and much more! 🍕
InfluxDB - Purpose built for real-time analytics at any scale.
Do you think we are missing an alternative of httpexpect or a related project?
README
httpexpect
Concise, declarative, and easy to use end-to-end HTTP and REST API testing for Go (golang).
Basically, httpexpect is a set of chainable builders for HTTP requests and assertions for HTTP responses and payload, on top of net/http and several utility packages.
Workflow:
- Incrementally build HTTP requests.
- Inspect HTTP responses.
- Inspect response payload recursively.
Features
Request builder
- URL path construction, with simple string interpolation provided by
go-interpol
package. - URL query parameters (encoding using
go-querystring
package). - Headers, cookies, payload: JSON, urlencoded or multipart forms (encoding using
form
package), plain text. - Custom reusable request builders and request transformers.
Response assertions
- Response status, predefined status ranges.
- Headers, cookies, payload: JSON, JSONP, forms, text.
- Round-trip time.
- Custom reusable response matchers.
Payload assertions
- Type-specific assertions, supported types: object, array, string, number, boolean, null, datetime.
- Regular expressions.
- Simple JSON queries (using subset of JSONPath), provided by
jsonpath
package. - JSON Schema validation, provided by
gojsonschema
package.
WebSocket support (thanks to @tyranron)
- Upgrade an HTTP connection to a WebSocket connection (we use
gorilla/websocket
internally). - Interact with the WebSocket server.
- Inspect WebSocket connection parameters and WebSocket messages.
Pretty printing
- Verbose error messages.
- JSON diff is produced on failure using
gojsondiff
package. - Failures are reported using
testify
(assert
orrequire
package) or standardtesting
package. - JSON values are pretty-printed using
encoding/json
, Go values are pretty-printed usinglitter
. - Dumping requests and responses in various formats, using
httputil
,http2curl
, or simple compact logger.
Tuning
- Tests can communicate with server via real HTTP client or invoke
net/http
orfasthttp
handler directly. - User can provide custom HTTP client, WebSocket dialer, HTTP request factory (e.g. from the Google App Engine testing).
- User can configure formatting options or provide custom templates based on
text/template
engine. - Custom handlers may be provided for logging, printing requests and responses, handling succeeded and failed assertions.
Versions
The versions are selected according to the semantic versioning scheme. Every new major version gets its own stable branch with a backwards compatibility promise. Releases are tagged from stable branches.
The current stable branch is v2
. Previous branches are still maintained, but no new features are added.
If you're using go.mod, use a versioned import path:
import "github.com/gavv/httpexpect/v2"
Otherwise, use gopkg.in import path:
import "gopkg.in/gavv/httpexpect.v2"
Documentation
Documentation is available on pkg.go.dev. It contains an overview and reference.
Examples
See [_examples
](_examples) directory for complete standalone examples.
[
fruits_test.go
](_examples/fruits_test.go)Testing a simple CRUD server made with bare
net/http
.[
iris_test.go
](_examples/iris_test.go)Testing a server made with
iris
framework. Example includes JSON queries and validation, URL and form parameters, basic auth, sessions, and streaming. Tests invoke thehttp.Handler
directly.[
echo_test.go
](_examples/echo_test.go)Testing a server with JWT authentication made with
echo
framework. Tests use either HTTP client or invoke thehttp.Handler
directly.[
gin_test.go
](_examples/gin_test.go)Testing a server utilizing the
gin
web framework. Tests invoke thehttp.Handler
directly.[
fasthttp_test.go
](_examples/fasthttp_test.go)Testing a server made with
fasthttp
package. Tests invoke thefasthttp.RequestHandler
directly.[
websocket_test.go
](_examples/websocket_test.go)Testing a WebSocket server based on
gorilla/websocket
. Tests invoke thehttp.Handler
orfasthttp.RequestHandler
directly.[
gae_test.go
](_examples/gae_test.go)Testing a server running under the Google App Engine.
Quick start
Hello, world!
package example
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gavv/httpexpect/v2"
)
func TestFruits(t *testing.T) {
// create http.Handler
handler := FruitsHandler()
// run server using httptest
server := httptest.NewServer(handler)
defer server.Close()
// create httpexpect instance
e := httpexpect.Default(t, server.URL)
// is it working?
e.GET("/fruits").
Expect().
Status(http.StatusOK).JSON().Array().Empty()
}
JSON
orange := map[string]interface{}{
"weight": 100,
}
e.PUT("/fruits/orange").WithJSON(orange).
Expect().
Status(http.StatusNoContent).NoContent()
e.GET("/fruits/orange").
Expect().
Status(http.StatusOK).
JSON().Object().ContainsKey("weight").ValueEqual("weight", 100)
apple := map[string]interface{}{
"colors": []interface{}{"green", "red"},
"weight": 200,
}
e.PUT("/fruits/apple").WithJSON(apple).
Expect().
Status(http.StatusNoContent).NoContent()
obj := e.GET("/fruits/apple").
Expect().
Status(http.StatusOK).JSON().Object()
obj.Keys().ContainsOnly("colors", "weight")
obj.Value("colors").Array().Elements("green", "red")
obj.Value("colors").Array().Element(0).String().Equal("green")
obj.Value("colors").Array().Element(1).String().Equal("red")
obj.Value("colors").Array().First().String().Equal("green")
obj.Value("colors").Array().Last().String().Equal("red")
JSON Schema and JSON Path
schema := `{
"type": "array",
"items": {
"type": "object",
"properties": {
...
"private": {
"type": "boolean"
}
}
}
}`
repos := e.GET("/repos/octocat").
Expect().
Status(http.StatusOK).JSON()
// validate JSON schema
repos.Schema(schema)
// run JSONPath query and iterate results
for _, private := range repos.Path("$..private").Array().Iter() {
private.Boolean().False()
}
Forms
// post form encoded from struct or map
e.POST("/form").WithForm(structOrMap).
Expect().
Status(http.StatusOK)
// set individual fields
e.POST("/form").WithFormField("foo", "hello").WithFormField("bar", 123).
Expect().
Status(http.StatusOK)
// multipart form
e.POST("/form").WithMultipart().
WithFile("avatar", "./john.png").WithFormField("username", "john").
Expect().
Status(http.StatusOK)
URL construction
// construct path using ordered parameters
e.GET("/repos/{user}/{repo}", "octocat", "hello-world").
Expect().
Status(http.StatusOK)
// construct path using named parameters
e.GET("/repos/{user}/{repo}").
WithPath("user", "octocat").WithPath("repo", "hello-world").
Expect().
Status(http.StatusOK)
// set query parameters
e.GET("/repos/{user}", "octocat").WithQuery("sort", "asc").
Expect().
Status(http.StatusOK) // "/repos/octocat?sort=asc"
Headers
// set If-Match
e.POST("/users/john").WithHeader("If-Match", etag).WithJSON(john).
Expect().
Status(http.StatusOK)
// check ETag
e.GET("/users/john").
Expect().
Status(http.StatusOK).Header("ETag").NotEmpty()
// check Date
t := time.Now()
e.GET("/users/john").
Expect().
Status(http.StatusOK).Header("Date").AsDateTime().InRange(t, time.Now())
Cookies
// set cookie
t := time.Now()
e.POST("/users/john").WithCookie("session", sessionID).WithJSON(john).
Expect().
Status(http.StatusOK)
// check cookies
c := e.GET("/users/john").
Expect().
Status(http.StatusOK).Cookie("session")
c.Value().Equal(sessionID)
c.Domain().Equal("example.com")
c.Path().Equal("/")
c.Expires().InRange(t, t.Add(time.Hour * 24))
Regular expressions
// simple match
e.GET("/users/john").
Expect().
Header("Location").
Match("http://(.+)/users/(.+)").Values("example.com", "john")
// check capture groups by index or name
m := e.GET("/users/john").
Expect().
Header("Location").Match("http://(?P<host>.+)/users/(?P<user>.+)")
m.Index(0).Equal("http://example.com/users/john")
m.Index(1).Equal("example.com")
m.Index(2).Equal("john")
m.Name("host").Equal("example.com")
m.Name("user").Equal("john")
Redirection support
e.POST("/path").
WithRedirectPolicy(httpexpect.FollowAllRedirects).
WithMaxRedirects(5).
Expect().
Status(http.StatusOK)
e.POST("/path").
WithRedirectPolicy(httpexpect.DontFollowRedirects).
Expect().
Status(http.StatusPermanentRedirect)
Retry support
// default retry policy
e.POST("/path").
WithMaxRetries(5).
Expect().
Status(http.StatusOK)
// custom retry policy
e.POST("/path").
WithMaxRetries(5).
WithRetryPolicy(httpexpect.RetryAllErrors).
Expect().
Status(http.StatusOK)
// custom retry delays
e.POST("/path").
WithMaxRetries(5).
WithRetryDelay(time.Second, time.Minute).
Expect().
Status(http.StatusOK)
Subdomains and per-request URL
e.GET("/path").WithURL("http://example.com").
Expect().
Status(http.StatusOK)
e.GET("/path").WithURL("http://subdomain.example.com").
Expect().
Status(http.StatusOK)
WebSocket support
ws := e.GET("/mysocket").WithWebsocketUpgrade().
Expect().
Status(http.StatusSwitchingProtocols).
Websocket()
defer ws.Disconnect()
ws.WriteText("some request").
Expect().
TextMessage().Body().Equal("some response")
ws.CloseWithText("bye").
Expect().
CloseMessage().NoContent()
Reusable builders
e := httpexpect.Default(t, "http://example.com")
r := e.POST("/login").WithForm(Login{"ford", "betelgeuse7"}).
Expect().
Status(http.StatusOK).JSON().Object()
token := r.Value("token").String().Raw()
auth := e.Builder(func (req *httpexpect.Request) {
req.WithHeader("Authorization", "Bearer "+token)
})
auth.GET("/restricted").
Expect().
Status(http.StatusOK)
e.GET("/restricted").
Expect().
Status(http.StatusUnauthorized)
Reusable matchers
e := httpexpect.Default(t, "http://example.com")
// every response should have this header
m := e.Matcher(func (resp *httpexpect.Response) {
resp.Header("API-Version").NotEmpty()
})
m.GET("/some-path").
Expect().
Status(http.StatusOK)
m.GET("/bad-path").
Expect().
Status(http.StatusNotFound)
Request transformers
e := httpexpect.Default(t, "http://example.com")
myTranform := func(r* http.Request) {
// modify the underlying http.Request
}
// apply transformer to a single request
e.POST("/some-path").
WithTransformer(myTranform).
Expect().
Status(http.StatusOK)
// create a builder that applies transfromer to every request
myBuilder := e.Builder(func (req *httpexpect.Request) {
req.WithTransformer(myTranform)
})
myBuilder.POST("/some-path").
Expect().
Status(http.StatusOK)
Shared environment
e := httpexpect.Default(t, "http://example.com")
t.Run("/users", func(t *testing.T) {
obj := e.GET("/users").
Expect().
Status(http.StatusOK).JSON().Object()
// store user id for next tests
userID := obj.Path("$.users[1].id").String().Raw()
e.Env().Put("user1.id", userID)
})
t.Run("/user/{userId}", func(t *testing.T) {
// read user id from previous tests
userID := e.Env().GetString("user1.id")
e.GET("/user/{userId}").
WithPath("userId", userID)
Expect().
Status(http.StatusOK)
})
Custom config
e := httpexpect.WithConfig(httpexpect.Config{
// include test name in failures (optional)
TestName: t.Name(),
// prepend this url to all requests
BaseURL: "http://example.com",
// use http.Client with a cookie jar and timeout
Client: &http.Client{
Jar: httpexpect.NewJar(),
Timeout: time.Second * 30,
},
// use fatal failures
Reporter: httpexpect.NewRequireReporter(t),
// print all requests and responses
Printers: []httpexpect.Printer{
httpexpect.NewDebugPrinter(t, true),
},
})
Use HTTP handler directly
// invoke http.Handler directly using httpexpect.Binder
var handler http.Handler = myHandler()
e := httpexpect.WithConfig(httpexpect.Config{
// prepend this url to all requests, required for cookies
// to be handled correctly
BaseURL: "http://example.com",
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Transport: httpexpect.NewBinder(handler),
Jar: httpexpect.NewJar(),
},
})
// invoke fasthttp.RequestHandler directly using httpexpect.FastBinder
var handler fasthttp.RequestHandler = myHandler()
e := httpexpect.WithConfig(httpexpect.Config{
// prepend this url to all requests, required for cookies
// to be handled correctly
BaseURL: "http://example.com",
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Transport: httpexpect.NewFastBinder(handler),
Jar: httpexpect.NewJar(),
},
})
Per-request client or handler
e := httpexpect.Default(t, server.URL)
client := &http.Client{
Transport: &http.Transport{
DisableCompression: true,
},
}
// overwrite client
e.GET("/path").WithClient(client).
Expect().
Status(http.StatusOK)
// construct client that invokes a handler directly and overwrite client
e.GET("/path").WithHandler(handler).
Expect().
Status(http.StatusOK)
WebSocket dialer
// invoke http.Handler directly using websocket.Dialer
var handler http.Handler = myHandler()
e := httpexpect.WithConfig(httpexpect.Config{
BaseURL: "http://example.com",
Reporter: httpexpect.NewAssertReporter(t),
WebsocketDialer: httpexpect.NewWebsocketDialer(handler),
})
// invoke fasthttp.RequestHandler directly using websocket.Dialer
var handler fasthttp.RequestHandler = myHandler()
e := httpexpect.WithConfig(httpexpect.Config{
BaseURL: "http://example.com",
Reporter: httpexpect.NewAssertReporter(t),
WebsocketDialer: httpexpect.NewFastWebsocketDialer(handler),
})
Session support
// cookie jar is used to store cookies from server
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Jar: httpexpect.NewJar(), // used by default if Client is nil
},
})
// cookies are disabled
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Jar: nil,
},
})
TLS support
// use TLS with http.Transport
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
// accept any certificate; for testing only!
InsecureSkipVerify: true,
},
},
},
})
// use TLS with http.Handler
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Transport: &httpexpect.Binder{
Handler: myHandler,
TLS: &tls.ConnectionState{},
},
},
})
Proxy support
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL("http://proxy.example.com"),
},
},
})
Global time-out/cancellation
handler := FruitsHandler()
server := httptest.NewServer(handler)
defer server.Close()
ctx, cancel := context.WithCancel(context.Background())
e := WithConfig(Config{
BaseURL: server.URL,
Reporter: httpexpect.NewAssertReporter(t),
Context: ctx,
})
go func() {
time.Sleep(time.Duration(5)*time.Second)
cancel()
}()
e.GET("/fruits").
Expect().
Status(http.StatusOK)
Per-request time-out/cancellation
// per-request context
e.GET("/fruits").
WithContext(context.TODO()).
Expect().
Status(http.StatusOK)
// per-request timeout
e.GET("/fruits").
WithTimeout(time.Duration(5)*time.Second).
Expect().
Status(http.StatusOK)
// timeout combined with retries (timeout applies to each try)
e.POST("/fruits").
WithMaxRetries(5).
WithTimeout(time.Duration(10)*time.Second).
Expect().
Status(http.StatusOK)
Printing requests and responses
// print requests in short form, don't print responses
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Printers: []httpexpect.Printer{
httpexpect.NewCompactPrinter(t),
},
})
// print requests as curl commands that can be inserted into terminal
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Printers: []httpexpect.Printer{
httpexpect.NewCurlPrinter(t),
},
})
// print requests and responses in verbose form
// also print all incoming and outgoing websocket messages
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Printers: []httpexpect.Printer{
httpexpect.NewDebugPrinter(t, true),
},
})
Customize failure formatting
// customize formatting options
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Formatter: &httpexpect.DefaultFormatter{
DisablePaths: true,
DisableDiffs: true,
},
})
// customize formatting template
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Formatter: &httpexpect.DefaultFormatter{
SuccessTemplate: "...",
FailureTemplate: "...",
TemplateFuncs: template.FuncMap{ ... },
},
})
// provide custom formatter
e := httpexpect.WithConfig(httpexpect.Config{
Reporter: httpexpect.NewAssertReporter(t),
Formatter: &MyFormatter{},
})
Customize assertion handling
// enable printing of succeeded assertions
e := httpexpect.WithConfig(httpexpect.Config{
AssertionHandler: &httpexpect.DefaultAssertionHandler{
Formatter: &httpexpect.DefaultFormatter{},
Reporter: httpexpect.NewAssertReporter(t),
Logger: t, // specify logger to enable printing of succeeded assertions
},
})
// provide custom assertion handler
// here you can implement custom handling of succeeded and failed assertions
// this may be useful for integrating httpexpect with other testing libs
// if desired, you can completely ignore builtin Formatter, Reporter, and Logger
e := httpexpect.WithConfig(httpexpect.Config{
AssertionHandler: &MyAssertionHandler{},
})
Similar packages
Contributing
Feel free to report bugs, suggest improvements, and send pull requests! Please add documentation and tests for new features.
Install developer dependencies:
-
go install golang.org/x/tools/cmd/stringer@latest
Re-generate, build, lint, and test everything:
make
Run gofmt:
make fmt
Run go generate:
make gen
Run go mod tidy:
make tidy
License
[MIT](LICENSE)
*Note that all licence references and agreements mentioned in the httpexpect README section above
are relevant to that project's source code only.