All Projects → gavv → Httpexpect

gavv / Httpexpect

Licence: mit
End-to-end HTTP and REST API testing for Go.

Programming Languages

go
31211 projects - #10 most used programming language
Makefile
30231 projects

Projects that are alternatives of or similar to Httpexpect

Zerocode
A community-developed, free, open source, microservices API automation and load testing framework built using JUnit core runners for Http REST, SOAP, Security, Database, Kafka and much more. Zerocode Open Source enables you to create, change, orchestrate and maintain your automated test cases declaratively with absolute ease.
Stars: ✭ 482 (-73.53%)
Mutual labels:  rest, json, assertions
Kaizen Openapi Editor
Eclipse Editor for the Swagger-OpenAPI Description Language
Stars: ✭ 97 (-94.67%)
Mutual labels:  rest, json
Restson Rust
Easy-to-use REST client for Rust programming language
Stars: ✭ 93 (-94.89%)
Mutual labels:  rest, json
Invoice As A Service
💰 Simple invoicing service (REST API): from JSON to PDF
Stars: ✭ 106 (-94.18%)
Mutual labels:  rest, json
Huobi golang
Go SDK for Huobi Spot API
Stars: ✭ 76 (-95.83%)
Mutual labels:  rest, websocket
Api Client Generator
Angular REST API client generator from Swagger YAML or JSON file with camel case settigs
Stars: ✭ 92 (-94.95%)
Mutual labels:  rest, json
Flickr Sdk
Almost certainly the best Flickr API client in the world for node and the browser
Stars: ✭ 104 (-94.29%)
Mutual labels:  rest, json
Json Api Dart
JSON:API client for Dart/Flutter
Stars: ✭ 53 (-97.09%)
Mutual labels:  rest, json
Persianjsonplaceholder
Persian fake REST/GraphQL API for testing and prototyping.
Stars: ✭ 110 (-93.96%)
Mutual labels:  rest, json
Json Serverless
Transform a JSON file into a serverless REST API in AWS cloud
Stars: ✭ 108 (-94.07%)
Mutual labels:  rest, json
Fetch Plus
🐕 Fetch+ is a convenient Fetch API replacement with first-class middleware support.
Stars: ✭ 116 (-93.63%)
Mutual labels:  rest, json
Ejdb
🏂 EJDB 2.0 — Embeddable JSON Database engine C library. Simple XPath like query language (JQL). Websockets / Android / iOS / React Native / Flutter / Java / Dart / Node.js bindings. Docker image.
Stars: ✭ 1,187 (-34.82%)
Mutual labels:  json, websocket
Rest
☕ REST: Yoctoframework — https://rest.n2o.dev
Stars: ✭ 71 (-96.1%)
Mutual labels:  rest, json
Behat Api Extension
API extension for Behat, used to ease testing of JSON-based APIs
Stars: ✭ 92 (-94.95%)
Mutual labels:  rest, json
Gophergameserver
🏆 Feature packed, easy-to-use game server API for Go back-ends and Javascript clients. Tutorials and examples included!
Stars: ✭ 61 (-96.65%)
Mutual labels:  json, websocket
Swagger Express Ts
Generate and serve swagger.json
Stars: ✭ 102 (-94.4%)
Mutual labels:  rest, json
Finch
Scala combinator library for building Finagle HTTP services
Stars: ✭ 1,552 (-14.77%)
Mutual labels:  rest, json
Generator Http Fake Backend
Yeoman generator for building a fake backend by providing the content of JSON files or JavaScript objects through configurable routes.
Stars: ✭ 49 (-97.31%)
Mutual labels:  rest, json
Flask Restx
Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
Stars: ✭ 1,050 (-42.34%)
Mutual labels:  rest, json
Airdcpp Webclient
Communal peer-to-peer file sharing application for file servers/NAS devices
Stars: ✭ 106 (-94.18%)
Mutual labels:  rest, websocket

httpexpect GoDev Build Coveralls GitHub release

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.
  • 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.
  • 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 or fasthttp handler directly.
  • Custom HTTP client, logger, printer, and failure reporter may be provided by user.
  • Custom HTTP request factory may be provided, e.g. from the Google App Engine testing.

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 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.

  • 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.New(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").DateTime().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.New(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.New(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.New(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)
Custom config
e := httpexpect.WithConfig(httpexpect.Config{
	// 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),

	// use verbose logging
	Printers: []httpexpect.Printer{
		httpexpect.NewCurlPrinter(t),
		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.New(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)
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)

Similar packages

Contributing

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

Build code, run linters, run tests:

make

Format code:

make fmt

Run go mod tidy:

make tidy

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].