Home

Awesome

go-hit

Actions Status Coverage Status PkgGoDev GoDoc go-report go1.15

hit is an http integration test framework written in golang.

It is designed to be flexible as possible, but to keep a simple to use interface for developers.

So lets get started!

go get -u github.com/Eun/go-hit

package main

import (
    "net/http"
    . "github.com/Eun/go-hit"
)

func main() {
    MustDo(
        Description("Post to httpbin.org"),
        Get("https://httpbin.org/post"),
        Expect().Status().Equal(http.StatusMethodNotAllowed),
        Expect().Body().String().Contains("Method Not Allowed"),
    )
}

Or use the Test() function:

package main_test
import (
    "testing"
    "net/http"
    . "github.com/Eun/go-hit"
)

func TestHttpBin(t *testing.T) {
    Test(t,
        Description("Post to httpbin.org"),
        Get("https://httpbin.org/post"),
        Expect().Status().Equal(http.StatusMethodNotAllowed),
        Expect().Body().String().Contains("Method Not Allowed"),
    )
}

Expect, Expect, Expect, ....

MustDo(
    Get("https://httpbin.org/post"),
    Expect().Status().Equal(http.StatusMethodNotAllowed),
    Expect().Headers("Content-Type").NotEmpty(),
    Expect().Body().String().Contains("Method Not Allowed"),
)

Sending Data

MustDo(
    Post("https://httpbin.org/post"),
    Send().Body().String("Hello HttpBin"),
    Expect().Status().Equal(http.StatusOK),
    Expect().Body().String().Contains("Hello HttpBin"), 
)

Sending And Expecting JSON

MustDo(
    Post("https://httpbin.org/post"),
    Send().Headers("Content-Type").Add("application/json"),
    Send().Body().JSON(map[string][]string{"Foo": []string{"Bar", "Baz"}}),
    Expect().Status().Equal(http.StatusOK),
    Expect().Body().JSON().JQ(".json.Foo[1]").Equal("Baz"),
)

Storing Data From The Response

var name string
var roles []string
MustDo(
    Post("https://httpbin.org/post"),
    Send().Headers("Content-Type").Add("application/json"),
    Send().Body().JSON(map[string]interface{}{"Name": "Joe", "Roles": []string{"Admin", "Developer"}}),
    Expect().Status().Equal(http.StatusOK),
    Store().Response().Body().JSON().JQ(".json.Name").In(&name),
    Store().Response().Body().JSON().JQ(".json.Roles").In(&roles),
)
fmt.Printf("%s has %d roles\n", name, len(roles))

Problems? Debug!

MustDo(
    Post("https://httpbin.org/post"),
    Debug(),
    Debug().Response().Body(),
)

Handling Errors

It is possible to handle errors in a custom way.

func login(username, password string) error {
    err := Do(
         Get("https://httpbin.org/basic-auth/joe/secret"),
         Send().Headers("Authorization").Add("Basic " + base64.StdEncoding.EncodeToString([]byte(username + ":" + password))),
         Expect().Status().Equal(http.StatusOK),
    )
    var hitError *Error
    if errors.As(err, &hitError) {
        if hitError.FailingStepIs(Expect().Status().Equal(http.StatusOK)) {
            return errors.New("login failed")
        }
    }
    return err
}

Build the request url manually

MustDo(
    Request().Method(http.MethodPost),
    Request().URL().Scheme("https"),
    Request().URL().Host("httpbin.org"),
    Request().URL().Path("/post"),
    Request().URL().Query("page").Add(1),
    Expect().Status().Equal(200),
    Send().Body().String("Hello World"),
    Expect().Body().String().Contains("Hello"),
)

Twisted!

Although the following is hard to read it is possible to do!

MustDo(
    Post("https://httpbin.org/post"),
    Expect().Status().Equal(200),
    Send().Body().String("Hello World"),
    Expect().Body().String().Contains("Hello"),
)

Custom Send And Expects

MustDo(
    Get("https://httpbin.org/get"),
    Send().Custom(func(hit Hit) error {
        hit.Request().Body().SetStringf("Hello %s", "World")
        return nil
    }),
    Expect().Custom(func(hit Hit) error {
        if len(hit.Response().Body().MustString()) <= 0 {
            return errors.New("expected the body to be not empty")
        }
        return nil
    }),
    Custom(AfterExpectStep, func(Hit) error {
        fmt.Println("everything done")
        return nil
    }),
)

Templates / Multiuse

template := CombineSteps(
    Post("https://httpbin.org/post"),
    Send().Headers("Content-Type").Add("application/json"),
    Expect().Headers("Content-Type").Equal("application/json"),
)
MustDo(
    template,
    Send().Body().JSON("Hello World"),
)

MustDo(
    template,
    Send().Body().JSON("Hello Universe"),
)

Clean Previous Steps

Sometimes it is necessary to remove some steps that were added before.

template := CombineSteps(
    Get("https://httpbin.org/basic-auth/joe/secret"),
    Expect().Status().Equal(http.StatusOK),
)
MustDo(
    Description("login with correct credentials"),
    template,
    Send().Headers("Authorization").Add("Basic " + base64.StdEncoding.EncodeToString([]byte("joe:secret"))),
)

Test(t,
    Description("login with incorrect credentials"),
    template,
    Clear().Expect().Status(),
    Expect().Status().Equal(http.StatusUnauthorized),
    Send().Headers("Authorization").Add("Basic " + base64.StdEncoding.EncodeToString([]byte("joe:joe"))),
)

More examples can be found in the examples directory

Changelog

0.5.0

0.4.0