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

Overview

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")
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{
	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{
	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{},
		},
	},
})

Similar packages

Contributing

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

Update dependencies, build code, and run tests and linters:

$ make

Format code:

$ make fmt

License

MIT

Comments
  • Default Formatter Implementation - followup

    Default Formatter Implementation - followup

    Based on https://github.com/gavv/httpexpect/pull/78

    • Uses *Context everywhere instead of Reporter. However this introduces breaking changes.
    • Changes from #78 comments.

    @gavv @goku321

    opened by fpeterschmitt 20
  • Refactor formatValue

    Refactor formatValue

    Issue: (part of) #190

    Refactoring before proceeding to introduce DefaultFormatter.DisableScientific as mentioned in https://github.com/gavv/httpexpect/issues/190#issuecomment-1356196337.

    Refactoring (more detail https://github.com/gavv/httpexpect/issues/190#issuecomment-1356181798):

    • Delete formatFloat. Replace with formatValue
    • Rework formatTyped to use formatValue
    • Rework formatRange to use formatValue
    • Introduce scientific as default float format

    Tasks:

    • [x] Add unit test to catch potential breaking early
    • [x] Refactor
    • [x] Re-evaluate unit test

    ~PR will be draft, unless all tasks are checked~

    PR is now ready

    opened by neverbeenthisweeb 18
  • Add unit tests for redirect support

    Add unit tests for redirect support

    Issue: https://github.com/gavv/httpexpect/issues/91

    Scope:

    • Add round tripper implementation that can have various HTTP response in unit tests
    • Add unit tests for redirects
    opened by neverbeenthisweeb 18
  • Add Google App Engine Testing Instance support (with RequestFactory interface{})

    Add Google App Engine Testing Instance support (with RequestFactory interface{})

    This PR adds support for Google App Engine Testing.

    Background

    Each *http.Request needs to be registered with the GAE test instance. Otherwise calls to appengine.NewContext(*http.Request) will panic with:

    appengine: NewContext passed an unknown http.Request
    

    References

    • https://github.com/golang/appengine/blob/master/aetest/instance_vm.go#L60
    • https://cloud.google.com/appengine/docs/go/tools/localunittesting/

    Changes

    • Add GaeTestInstance to struct Config
    • If Config.GaeTestInstance is specified (not nil), use GaeTestInstance.NewRequest to create *http.Request that is associated with the GAE test instance.
    • Otherwise create plain *http.Request.
    • In order to keep the correct reference, struct Request needs pointer to request.
    opened by mattes 14
  • Rework error reporting and formatting

    Rework error reporting and formatting

    Problem

    Currently we're missing the following features:

    • the user can't register a hook that will be invoked on every performed assertion including successful ones; e.g. this could be used for goconvey integration (#143) or for other reasons;

    • when an error is passed to user-defined Reporter, it only receives the error message, but no additional context: e.g. test name (#59), request and response (#137), value which didn't pass the assertion, etc;

    • since every component formats error message by its own, there is no single point where we can implement various formatting improvements like colored output.

    Solution

    The suggested solution is to unify error handling by introducing error context and formatter:

    • Add Context struct:

      • Context will accumulate the assertion context, assertion name (i.e. method name like "Equal"), request, response, round trip time, etc. E.g., when the user calls Expect(), the encoded request and received response are stored into context.

      • Context should be inherited by nested objects, e.g. in Array().Element() the returned element inherits the context of the array.

      • Context will be passed to Formatter when an assertion fails.

      • Context will contain Reporter field, which will be used to report failures.

      • Context will also contain private Formatter field, to allow nested components to inherit formatter.

      • We already have struct chain which is automatically inherited by all nested objects. We should just replace chain.reporter with chain.context.

    • Add Failure struct:

      • Failure will contain information about the failed assertion: error message, actual value, and expected value (for assertions like Equal) or the value which triggered the failure (for assertions like Contains).

      • Failure will be passed to Formatter when an assertion fails.

    • Add Formatter interface with four methods:

      BeginAssertion(Context)
      Success(Context)
      Failure(Context, Failure)
      EndAssertion(Context)
      
    • Add DefaultFormatter implementation of the Formatter interface:

      • BeginAssertion(), EndAssertion(), and Success() may be no-op for now.

      • Failure() should format info from Context and Failure structs into a string and pass it to Context.Reporter.

      • It should use dumpValue() to print values from Failure struct. If both actual and expected values are present, it should use diffValues() to print the diff. Currently this logic is duplicated in every component and now it should be implemented once in Formatter.

    • Switch all components to new structs:

      • Make all components using Context.

      • Add Formatter to Config. If it's nil, automatically use DefaultFormatter. Pass Formatter and Reporter from Config to to Context.

      • Incrementally, one-by-one, switch every component from doing its own error formatting and reporting to using Context.Formatter. They should call BeginAssertion() before doing a check, then call Success() and Failure(), and then call EndAssertion().

    Notes

    Since these are quite large changes, it's better to split the implementation into multiple PRs, like:

    1. Add Context, Failure, and Formatter types.
    2. Add DefaultFormatter.
    3. Use Context in all components.
    4. Use Formatter in every component (this step can be split into multiple PRs as well, e.g. one PR for one or a few components).

    Preferably we should not break public API. For now it seems that we can do it. Otherwise we'll have to make v3 branch.

    Further improvements

    After finishing this issue, we'll be able to implement the following improvements:

    • allow to assign a name to every test and then print it on failure;
    • pretty-print request, response, and RTT on failure;
    • print backtrace on failure;
    • colored output, including colored diff.

    When this task is done, we'll create new issues for these features.

    refactoring 
    opened by gavv 13
  • Add unit tests for redirect and retry support

    Add unit tests for redirect and retry support

    Redirect methods (WithRedirectPolicy, WithMaxRedirects) and retry methods (WithRetryPolicy, WithMaxRetries, WithRetryDelay) are already covered with end-to-end tests (e2e_redirect_test.go, e2e_retry_test.go), but unit tests are missing.

    It would be nice to add unit tests for these methods to request_test.go. In unit tests, we can use http.Client with custom Transport set to our own implementation of http.RoundTripper interface. There we can check what requests are generated by httpexpect and produce fake responses to trigger redirects and retries.

    tests help wanted 
    opened by gavv 12
  • Implement Array.Transform and Object.Transform

    Implement Array.Transform and Object.Transform

    Add new methods to Array and Object that allow to run function for every element:

    func (a *Array) Transform(fn func(index int, value *httpexpect.Value) (*httpexpect.Value, error)) *Array
    ...
    array.Transform(func(index int, value *httpexpect.Value) (*httpexpect.Value, error) {
        // do something with value, for example:
        return httpexpect.NewValue(httpexpect.Reporter, value), nil
    })
    
    func (a *Object) Transform(fn func(key string, value *httpexpect.Value) (*httpexpect.Value, nil)) *Object
    ...
    object.Transform(func(key string, value *httpexpect.Value) *httpexpect.Value {
        // do something with value, for example:
        return httpexpect.NewValue(httpexpect.Reporter, value)
    })
    

    New methods should have documentation comment with example and be covered by unit tests.

    When array or object is constructing value to be passed to function, it should clone chain and append new path entry to it.

    valueChain := a.chain.clone()
    valueChain.replace("Transform[%q]", key)
    
    value := newValue(valueChain, o.value[key])
    

    Upon returning error or nil from the passed function Transform would use the original value in the new Array/Object

    enhancement 
    opened by angshumanHalder 11
  • Update to Iris version 12

    Update to Iris version 12

    Hello @gavv how are you?

    Iris has been updated to version 12.0.1 which requires a new semantic import path. Read here for more. This PR updates the [_examples/iris.go](_examples] to be compatible with Iris v12.0.1.

    Thanks, Gerasimos Maropoulos. Author of the Iris Web Framework.

    opened by kataras 11
  • Session support

    Session support

    Hi!

    First-off: awesome framework.

    Secondly, I have noticed that there's no support for session/cookie-data?

    I can login using

        e.POST("/login/").WithForm(data).
            Expect().
            Status(http.StatusFound)
    

    But on successive calls, the retrieved cookie is not being used. Seeing as most pages will require some kind of authentication, I would expect this feature to exist? (either now or in the future)

    enhancement 
    opened by EtienneBruines 11
  • Additional constructors for matcher type  (Value, Object, Array, etc)

    Additional constructors for matcher type (Value, Object, Array, etc)

    Signed-off-by: Rohith-Raju [email protected] Added new constructors for the following :

    1. Array
    2. Boolean
    3. Cookie
    4. Datetime
    5. Duration
    6. Match
    7. Number
    8. Object
    9. String
    10. Value

    Closes: #204

    opened by Rohith-Raju 10
  • I have issue when testing subdomains

    I have issue when testing subdomains

    Hello my friend, I have some issues when testing subdomains, the same time the browser returns status 200 ( with cleaned cache) the httpexpect returns 404, here is example code:

    
    const (
        scheme = "http://"
        domain = "mydomain.com"
        port   = 8080 // this will go as test flag some day.
    )
    
    var (
        addr = domain + ":" + strconv.Itoa(port)
        host = scheme + addr
    )
    
    // tester_lister starts the server to listen on, this is useful ONLY WHEN TEST (AND) SUBDOMAINS,
    // the hosts file (on windows) must be setted as '127.0.0.1 mydomain.com' & '127.0.0.1 mysubdomain.mydomain.com'
    func tester_listen(api *iris.Framework, t *testing.T) *httpexpect.Expect {
        api.Config.DisableBanner = true
        go api.Listen(addr)
        time.Sleep(time.Duration(10) * time.Second)
        return httpexpect.WithConfig(httpexpect.Config{
            BaseURL: host,
            Client: &http.Client{
                Transport: httpexpect.NewFastBinder(api.HTTPServer.Handler),
                Jar:       httpexpect.NewJar(),
            },
            Reporter: httpexpect.NewAssertReporter(t),
            Printers: []httpexpect.Printer{
                httpexpect.NewDebugPrinter(t, true),
            },
        })
    }
    
    
    // and the test:
            api := iris.New()
    //...
        mysubdomain := api.Party("mysubdomain.").Get("/get", func(ctx *iris.Context) {
            writeValues(ctx)
        })
        h := tester_listen(api, t)
    //...
                h.Request("GET", scheme+"mysubdomain."+addr+"/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values)
        //...
    
    

    As you see I put a real server running & time.Sleep of 10 seconds in order to have time to test it on my browser, there the subdomain worked, at the tests it shows me:

    printer.go:55: GET http://mysubdomain.mydomain.com:8080/get HTTP/1.1
                    Host: mydomain.com:8080
    
            printer.go:72: HTTP/0.0 404 Not Found 0
                    Content-Type: text/plain; charset=utf-8
    
                    Not Found
            Error Trace:    reporter.go:22
                            chain.go:21
                            response.go:437
                            response.go:123
                            sessions_test.go:63
            Error:
                            expected status equal to:
                             "200 OK"
    
                            but got:
                             "404 Not Found"
    
    

    Complete code:

    // file ./test/iris_test.go
    //Package test -v ./... builds all tests
    package test
    
    import (
        "net/http"
        "strconv"
        "testing"
    
        "github.com/gavv/httpexpect"
        "github.com/kataras/iris"
    )
    
    const (
        scheme = "http://"
        domain = "mydomain.com"
        port   = 8080 // this will go as test flag some day.
    
        // will start  the server to real listen , this is useful ONLY WHEN TEST (AND) SUBDOMAINS,
        // the hosts file (on windows) must be setted as '127.0.0.1 mydomain.com' & '127.0.0.1 mysubdomain.mydomain.com'
        enable_subdomains_tests = true // this will go as test flag some day also.
        subdomain               = "mysubdomain"
    )
    
    var (
        addr           = domain + ":" + strconv.Itoa(port)
        host           = scheme + addr
        subdomain_host = scheme + subdomain + "." + addr
    )
    
    func tester(api *iris.Framework, t *testing.T) *httpexpect.Expect {
        api.Config.DisableBanner = true
        go func() { // no need goroutine here, we could just add go api.Listen(addr) but newcomers can see easier that these will run in a non-blocking way
            if enable_subdomains_tests {
                api.Listen(addr)
            } else {
                api.NoListen(addr)
            }
        }()
    
        if ok := <-api.Available; !ok {
            panic("Unexpected error: server cannot start, please report this as bug!!")
        }
        close(api.Available)
    
        handler := api.HTTPServer.Handler
        return httpexpect.WithConfig(httpexpect.Config{
            BaseURL: host,
            Client: &http.Client{
                Transport: httpexpect.NewFastBinder(handler),
                Jar:       httpexpect.NewJar(),
            },
            Reporter: httpexpect.NewAssertReporter(t),
            Printers: []httpexpect.Printer{
                httpexpect.NewDebugPrinter(t, true),
            },
        })
    
    }
    
    
    //file ./test/sessions_test.go
    
    //Package test -v ./... builds all tests
    package test
    
    import (
        "testing"
    
        "github.com/kataras/iris"
    )
    
    func TestSessions(t *testing.T) {
        sessionId := "mycustomsessionid"
    
        values := map[string]interface{}{
            "Name":   "iris",
            "Months": "4",
            "Secret": "dsads£2132215£%%Ssdsa",
        }
    
        api := iris.New()
    
        api.Config.Sessions.Cookie = sessionId
        writeValues := func(ctx *iris.Context) {
            sessValues := ctx.Session().GetAll()
            ctx.JSON(iris.StatusOK, sessValues)
        }
    
        api.Post("set", func(ctx *iris.Context) {
            vals := make(map[string]interface{}, 0)
            if err := ctx.ReadJSON(&vals); err != nil {
                t.Fatalf("Cannot readjson. Trace %s", err.Error())
            }
            for k, v := range vals {
                ctx.Session().Set(k, v)
            }
        })
    
        api.Get("/get", func(ctx *iris.Context) {
            writeValues(ctx)
        })
    
        if enable_subdomains_tests {
            api.Party(subdomain+".").Get("/get", func(ctx *iris.Context) {
                writeValues(ctx)
            })
        }
    
        api.Get("/clear", func(ctx *iris.Context) {
            ctx.Session().Clear()
            writeValues(ctx)
        })
    
        api.Get("/destroy", func(ctx *iris.Context) {
            ctx.SessionDestroy()
            writeValues(ctx)
            // the cookie and all values should be empty
        })
    
        h := tester(api, t)
    
        h.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty()
        h.GET("/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values)
        if enable_subdomains_tests {
            h.Request("GET", subdomain_host+"/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values)
        }
    
        // test destory which also clears first
        d := h.GET("/destroy").Expect().Status(iris.StatusOK)
        d.JSON().Object().Empty()
        d.Cookies().ContainsOnly(sessionId)
        // set and clear again
        h.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty()
        h.GET("/clear").Expect().Status(iris.StatusOK).JSON().Object().Empty()
    
    }
    

    When enable_subdomains_tests is false the tests are passed*


    Thoughts: If I remove the BaseURL entirely I have two problems:

    • the h.GET(host + "/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values) returns empty json response , while it's not empty. Solution: if you check manually the domain field of a cookie header, keep note of the RFC2109, meaning that a domain field can be filled as .mydomain.com that's means that the mydomain.com AND all first-level subdomains have access to this cookie , iris sets the domain field to "."+domain in order to achieve the sessions persistence on subdomains, which is working well on all browsers)
    • I have to put the http://mydomain.com in frontof all the .GET/POST... and it is not a good idea, I think the BaseURL takes the host and all requests are declared as relative to it. Solution: you could check if the 'path' starts with http://, https://, ws:// then it is url and not a BaseURL's relative path.

    If you want to run the code by yourself or add some httpexpect fixes so I can learn from those, do that in iris-contrib/tests ( I updated the tests), thanks!!

    enhancement 
    opened by ghost 10
  • Extend fruits server example

    Extend fruits server example

    Extend example add new test for matching functions

    • Array.Iter
    • Array.Every
    • Array.ContainsAny
    • Object.ContainsKey
    • Object.ContainsValue
    • Object.ContainsSubset
    • String.HasPrefix and String.HasSuffix
    • String.IsASCII
    ready for review 
    opened by kairemor 1
  • Add DefaultFormatter.DisableScientific

    Add DefaultFormatter.DisableScientific

    Issue: https://github.com/gavv/httpexpect/issues/190

    Tasks:

    • [x] Add DisableScientific flag in DefaultFormatter
    • [x] Add/update sample unit test (for early review)
    • [ ] Add/update remaining unit test (if sample is ok)
    work in progress 
    opened by neverbeenthisweeb 4
  • Add custom format template example

    Add custom format template example

    Add a new example to _examples directory that uses DefaultFormatter with custom FailureTemplate and TemplateFuncs fields.

    I think it's a good idea to provide example of formatting assertions in JSON format, but other ideas are welcome too.

    Add link to new example to README.

    help wanted examples 
    opened by gavv 0
  • Extend fruits server example

    Extend fruits server example

    We have this example of using httpexpect: https://github.com/gavv/httpexpect/blob/master/_examples/fruits_test.go

    Recently we have added some new matching functions and it would be nice to demonstrate their usage in that example.

    It would be nice to cover some of these methods:

    • Array.Iter, Object.Iter
    • Array.Every, Object.Every
    • Array.ContainsAny
    • Object.ContainsKey
    • Object.ContainsValue
    • Object.ContainsSubset
    • String.HasPrefix or String.HasSuffix
    • String.IsASCII
    • String.AsNumber
    • String.AsBoolean

    Currently there is single test TestFruits. It makes sense to split it into a few tests that cover different sets of methods, e.g. object, array, string, etc.

    help wanted good first issue examples 
    opened by gavv 1
  • Add linter for code examples formatting

    Add linter for code examples formatting

    I'm not sure if such linter exists, but it would be awesome to have one, because formatting of examples is so often broken in PRs. httpexpect has a lot of code examples, and it's quite annoying to review formatting issues manually.

    This formatting is GOOD:

    // Some text.
    //
    // Example:
    //
    //	someCode()
    func SomeFunc() { ... }
    

    Below I list examples of BAD formatting to be detected by linter.

    It would be great if we could detect at least part of these problems automatically.

    BAD: no space after //:

    //Some text.
    //
    // Example:
    //
    //	someCode()
    func SomeFunc() { ... }
    

    BAD: missing empty line before Example:

    // Some text.
    // Example:
    //
    //	someCode()
    func SomeFunc() { ... }
    

    BAD: missing empty line after Example:

    // Some text.
    //
    // Example:
    //	someCode()
    func SomeFunc() { ... }
    

    BAD: forgetting to indent example code:

    // Some text.
    //
    // Example:
    //
    // someCode()
    func SomeFunc() { ... }
    

    BAD: using spaces instead of TAB to indent example code:

    // Some text.
    //
    // Example:
    //
    //  someCode()
    func SomeFunc() { ... }
    

    BAD: extra empty line between comment and function:

    // Some text.
    //
    // Example:
    //
    //	someCode()
    
    func SomeFunc() { ... }
    
    help wanted linters 
    opened by gavv 0
Owner
Victor Gaydov
Victor Gaydov
An always-on framework that performs end-to-end functional network testing for reachability, latency, and packet loss

Arachne Arachne is a packet loss detection system and an underperforming path detection system. It provides fast and easy active end-to-end functional

Uber Open Source 370 Dec 31, 2022
🚀🌏 Orbital is a simple end-to-end testing framework for Go

Orbital is a test framework which enables a developer to write end to end tests just like one would writing unit tests. We do this by effectively copying the testing.T API and registering tests to be run periodically on a configured schedule.

Segment 79 Nov 18, 2022
A simple and expressive HTTP server mocking library for end-to-end tests in Go.

mockhttp A simple and expressive HTTP server mocking library for end-to-end tests in Go. Installation go get -d github.com/americanas-go/mockhttp Exa

Americanas Go 6 Dec 19, 2021
End to end functional test and automation framework

Declarative end to end functional testing (endly) This library is compatible with Go 1.12+ Please refer to CHANGELOG.md if you encounter breaking chan

Viant, Inc 231 Jan 6, 2023
Rr-e2e-tests - Roadrunner end-to-end tests repository

RoadRunner end-to-end plugins tests License: The MIT License (MIT). Please see L

RoadRunner 3 Dec 15, 2022
Hsuan-Fuzz: REST API Fuzzing by Coverage Level Guided Blackbox Testing

Hsuan-Fuzz: REST API Fuzzing by Coverage Level Guided Blackbox Testing Architecture Usage package main import ( restAPI "github.com/iasthc/hsuan-

Chung-Hsuan Tsai 20 Nov 30, 2022
HTTP mock for Golang: record and replay HTTP/HTTPS interactions for offline testing

govcr A Word Of Warning I'm in the process of partly rewriting govcr to offer better support for cassette mutations. This is necessary because when I

Seb C 141 Dec 28, 2022
siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

Re 296 Dec 12, 2022
A yaml data-driven testing format together with golang testing library

Specimen Yaml-based data-driven testing Specimen is a yaml data format for data-driven testing. This enforces separation between feature being tested

Design it, Run it 1 Nov 24, 2022
Extremely flexible golang deep comparison, extends the go testing package and tests HTTP APIs

go-testdeep Extremely flexible golang deep comparison, extends the go testing package. Latest news Synopsis Description Installation Functions Availab

Maxime Soulé 334 Dec 22, 2022
HTTP traffic mocking and testing made easy in Go ༼ʘ̚ل͜ʘ̚༽

gock Versatile HTTP mocking made easy in Go that works with any net/http based stdlib implementation. Heavily inspired by nock. There is also its Pyth

Tom 1.8k Jan 4, 2023
HTTP load testing tool and library. It's over 9000!

Vegeta Vegeta is a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate. It can be used both as a

Tomás Senart 20.6k Jan 7, 2023
Golang HTTP client testing framework

flute Golang HTTP client testing framework Presentation https://speakerdeck.com/szksh/flute-golang-http-client-testing-framework Overview flute is the

Shunsuke Suzuki 17 Sep 27, 2022
Ditto is a CLI testing tool that helps you verify if multiple HTTP endpoints have the same outputs.

Ditto is a CLI testing tool that helps you verify if multiple HTTP endpoints have the same outputs.

Cristopher 1 Nov 24, 2021
Tesuto - a little library for testing against HTTP services

tesuto import "github.com/guregu/tesuto" tesuto is a little library for testing

Greg 2 Jan 18, 2022
Client tool for testing HTTP server timeouts

HTTP timeout test client While testing Go HTTP server timeouts I wrote this little tool to help me test. It allows for slowing down header write and b

Adam Pritchard 8 Sep 21, 2022
API testing framework inspired by frisby-js

frisby REST API testing framework inspired by frisby-js, written in Go Proposals I'm starting to work on frisby again with the following ideas: Read s

_Hofstadter 274 Sep 27, 2022
Testing API Handler written in Golang.

Gofight API Handler Testing for Golang Web framework. Support Framework Http Handler Golang package http provides HTTP client and server implementatio

Bo-Yi Wu 428 Dec 16, 2022
A Go library help testing your RESTful API application

RESTit A Go micro-framework to help writing RESTful API integration test Package RESTit provides helps to those who want to write an integration test

RESTit 55 Oct 28, 2022