MCPcopy
hub / github.com/gavv/httpexpect

github.com/gavv/httpexpect @v2.17.0 sqlite

repository ↗ · DeepWiki ↗ · release v2.17.0 ↗
1,362 symbols 10,127 edges 95 files 613 documented · 45%
README

httpexpect GoDev Build Coveralls GitHub release Discord

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
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, duration, cookie.
  • 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 or require package) or standard testing package.
  • JSON values are pretty-printed using encoding/json, Go values are pretty-printed using litter.
  • Dumping requests and responses in various formats, using httputil, http2curl, or simple compact logger.
  • Printing stacktrace on failure in verbose or compact format.
  • Color support using fatih/color.
Tuning
  • Tests can communicate with server via real HTTP client or invoke net/http or fasthttp handler directly.
  • User can provide custom HTTP client, WebSocket dialer, HTTP request factory (e.g. from the Google App Engine testing).
  • User can configure redirect and retry policies and timeouts.
  • User can configure formatting options (what parts to display, how to format numbers, etc.) 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.

Versioning

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.

Changelog file can be found here: changelog.

The current stable branch is v2:

import "github.com/gavv/httpexpect/v2"

Documentation

Documentation is available on pkg.go.dev. It contains an overview and reference.

Community

Community forum and Q&A board is right on GitHub in discussions tab.

For more interactive discussion, you can join discord chat.

Contributing

Feel free to report bugs, suggest improvements, and send pull requests! Please add documentation and tests for new features.

This project highly depends on contributors. Thank you all for your amazing work!

If you would like to submit code, see HACKING.md.

Donating

If you would like to support my open-source work, you can do it here:

Thanks!

Examples

See _examples directory for complete standalone examples.

  • fruits_test.go

    Testing a simple CRUD server made with bare net/http.

  • 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 the http.Handler directly.

  • echo_test.go

    Testing a server with JWT authentication made with echo framework. Tests use either HTTP client or invoke the http.Handler directly.

  • gin_test.go

    Testing a server utilizing the gin web framework. Tests invoke the http.Handler directly.

  • fasthttp_test.go

    Testing a server made with fasthttp package. Tests invoke the fasthttp.RequestHandler directly.

  • websocket_test.go

    Testing a WebSocket server based on gorilla/websocket. Tests invoke the http.Handler or fasthttp.RequestHandler directly.

  • tls_test.go

Testing a TLS server made with net/http and crypto/tls

Testing a OAuth2 server with oauth2.

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().IsEmpty()
}
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").HasValue("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().ConsistsOf("green", "red")
obj.Value("colors").Array().Value(0).String().IsEqual("green")
obj.Value("colors").Array().Value(1).String().IsEqual("red")
obj.Value("colors").Array().First().String().IsEqual("green")
obj.Value("colors").Array().Last().String().IsEqual("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().IsFalse()
}
JSON decoding
type User struct {
    Name   string `json:"name"`
    Age    int    `json:"age"`
    Gender string `json:"gender"`
}

var user User
e.GET("/user").
    Expect().
    Status(http.StatusOK).
    JSON().
    Decode(&user)

if user.Name != "octocat" {
    t.Fail()
}
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().IsEqual(sessionID)
c.Domain().IsEqual("example.com")
c.Path().IsEqual("/")
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.Submatch(0).IsEqual("http://example.com/users/john")
m.Submatch(1).IsEqual("example.com")
m.Submatch(2).IsEqual("john")

m.NamedSubmatch("host").IsEqual("example.com")
m.NamedSubmatch("user").IsEqual("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 built-in 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)

// custom user-defined retry policy
e.POST("/path").
    WithMaxRetries(5).
    WithRetryPolicyFunc(func(resp *http.Response, err error) bool {
        return resp.StatusCode == http.StatusTeapot
    }).
    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().IsEqual("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

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

Extension points exported contracts — how you extend this code

Reporter (Interface)
Reporter is used to report failures. *testing.T, FatalReporter, AssertReporter, RequireReporter, PanicReporter implement [8 …
expect.go
Printer (Interface)
Printer is used to print requests and responses. CompactPrinter, DebugPrinter, and CurlPrinter implement this interface. [5 …
printer.go
AssertionHandler (Interface)
AssertionHandler takes care of formatting and reporting test Failure or Success. You can log every performed assertion, [4 …
assertion.go
Formatter (Interface)
Formatter is used to format assertion messages into strings. [2 implementers]
formatter.go
WebsocketConn (Interface)
WebsocketConn is used by Websocket to communicate with actual WebSocket connection. [1 implementers]
websocket.go
RequestFactory (Interface)
RequestFactory is used to create all http.Request objects. aetest.Instance from the Google App Engine implements this in [2 …
expect.go
WebsocketPrinter (Interface)
WebsocketPrinter is used to print writes and reads of WebSocket connection. If WebSocket connection is used, all Printe [2 …
printer.go
Logger (Interface)
Logger is used as output backend for Printer. *testing.T implements this interface. [2 implementers]
expect.go

Core symbols most depended-on inside this repo

Equal
called by 1160
value.go
assert
called by 1153
chain.go
leave
called by 385
chain.go
enter
called by 384
chain.go
fail
called by 383
chain.go
failed
called by 321
chain.go
Expect
called by 296
request.go
Raw
called by 227
value.go

Shape

Method 613
Function 595
Struct 100
TypeAlias 37
Interface 12
FuncType 5

Languages

Go100%

Modules by API surface

request.go70 symbols
array.go52 symbols
request_test.go50 symbols
mocks_test.go49 symbols
string.go48 symbols
expect.go48 symbols
response.go46 symbols
object.go46 symbols
formatter.go40 symbols
datetime.go39 symbols
websocket.go37 symbols
number.go37 symbols

Dependencies from manifests, versioned

github.com/CloudyKit/fastprinterv0.0.0-2020010918263 · 1×
github.com/CloudyKit/jet/v6v6.3.1 · 1×
github.com/Joker/jadev1.1.3 · 1×
github.com/Shopify/goreferrerv0.0.0-2024072416510 · 1×
github.com/TylerBrock/colorjsonv0.0.0-2020070600362 · 1×
github.com/ajg/formv1.5.1 · 1×
github.com/andybalholm/brotliv1.0.4 · 1×
github.com/aymerick/douceurv0.2.0 · 1×
github.com/bytedance/sonicv1.12.10 · 1×
github.com/bytedance/sonic/loaderv0.2.3 · 1×
github.com/cloudwego/base64xv0.1.5 · 1×

For agents

$ claude mcp add httpexpect \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact