requests
To configure the initial request into the system under test, you can specify request parameters such as the http method, url, headers and cookies.
apitest.New().
Handler(handler).
Method(http.MethodGet).
URL("/user/12345")
This is quite verbose, so there are some shortcuts defined for the common http verbs that wrap up the Method and URL functions. The example can be more concisely defined as
apitest.Handler(handler).
Get("/user/12345")
You can also define the request using the standard Go http.Request
type.
req := httptest.NewRequest(http.MethodGet, "/user/1234", nil)
apitest.Handler(handler).
Request(req)
Basic Auth
A helper method is provided to add preemptive basic authentication to the request.
BasicAuth("username", "password")
package main
import (
"net/http"
"testing"
"github.com/steinfletcher/apitest"
)
func TestRequests_BasicAuth(t *testing.T) {
handler := func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok {
w.WriteHeader(http.StatusBadRequest)
return
}
if username != "username" || password != "password" {
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}
apitest.New().
HandlerFunc(handler).
Get("/hello").
BasicAuth("username", "password").
Expect(t).
Status(http.StatusOK).
End()
}
Body
There are two methods to set the request body - Body
and JSON
. Using Body
the data will be copied to the raw request and wrapped in an io.Reader
.
Post("/message").Body("hello")
JSON
does the same and copies the provided data to the body, but the JSON
method also sets the content type to application/json
.
Post("/chat").JSON(`{"message": "hi"}`)
If you want to define other content types set the body using Body(data)
and the header using Header
.
Post("/path").
Body("<html>content</html>").
Header("Content-Type", "text/html")
package main
import (
"io/ioutil"
"net/http"
"testing"
"github.com/steinfletcher/apitest"
)
func TestRequests_JSONBody(t *testing.T) {
handler := func(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
if string(data) != `{"a": 12345}` {
w.WriteHeader(http.StatusInternalServerError)
return
}
if r.Header.Get("Content-Type") != "application/json" {
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}
apitest.New().
HandlerFunc(handler).
Post("/hello").
JSON(`{"a": 12345}`).
Expect(t).
Status(http.StatusOK).
End()
}
package main
import (
"io/ioutil"
"net/http"
"testing"
"github.com/steinfletcher/apitest"
)
func TestRequests_TextBody(t *testing.T) {
handler := func(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
if string(data) != `hello` {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
apitest.New().
HandlerFunc(handler).
Put("/hello").
Body(`hello`).
Expect(t).
Status(http.StatusOK).
End()
}
Form
There are multiple ways to create a URL encoded form body in the request. The following approaches are chainable.
Multiple values
FormData is a variadic function that can be used to take a variable amount of values for the same key.
FormData("name", "value1", "value2")
Short form
FormData("name", "value")
GraphQL
The following helpers simplify building GraphQL requests.
Post("/graphql").
GraphQLQuery(`query { todos { text } }`).
Post("/graphql").
GraphQLRequest(apitest.GraphQLRequestBody{
Query: "query someTest($arg: String!) { test(who: $arg) }",
Variables: map[string]interface{}{
"arg": "myArg",
},
OperationName: "myOperation",
}).
package main
import (
"encoding/json"
"io/ioutil"
"net/http"
"testing"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/assert"
)
func TestRequests_GraphQLQuery(t *testing.T) {
apitest.New().
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Fatal(err)
}
var req apitest.GraphQLRequestBody
if err := json.Unmarshal(bodyBytes, &req); err != nil {
t.Fatal(err)
}
assert.Equal(t, apitest.GraphQLRequestBody{
Query: `query { todos { text } }`,
}, req)
w.WriteHeader(http.StatusOK)
}).
Post("/query").
GraphQLQuery(`query { todos { text } }`).
Expect(t).
Status(http.StatusOK).
End()
}
func TestRequests_GraphQLRequest(t *testing.T) {
apitest.New().
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Fatal(err)
}
var req apitest.GraphQLRequestBody
if err := json.Unmarshal(bodyBytes, &req); err != nil {
t.Fatal(err)
}
expected := apitest.GraphQLRequestBody{
Query: `query { todos { text } }`,
OperationName: "myOperation",
Variables: map[string]interface{}{
"a": float64(1),
"b": "2",
},
}
assert.Equal(t, expected, req)
w.WriteHeader(http.StatusOK)
}).
Post("/query").
GraphQLRequest(apitest.GraphQLRequestBody{
Query: "query { todos { text } }",
Variables: map[string]interface{}{
"a": 1,
"b": "2",
},
OperationName: "myOperation",
}).
Expect(t).
Status(http.StatusOK).
End()
}
Headers
There are multiple ways to specify http request headers. The following approaches are chainable.
Map
Headers(map[string]string{"name1": "value1", "name2": "value2"})
Params
Header("name", "value")
Intercept
Intercept
is invoked pre request, allowing the implementer to mutate the request object before it is sent to the system under test. In this example we set the request params with a custom scheme.
package main
import (
"net/http"
"testing"
"github.com/steinfletcher/apitest"
)
func TestIntercept(t *testing.T) {
handler := func(w http.ResponseWriter, r *http.Request) {
if r.URL.RawQuery != "a[]=xxx&a[]=yyy" {
t.Fatal("unexpected query")
}
w.WriteHeader(http.StatusOK)
}
apitest.New().
HandlerFunc(handler).
Intercept(func(req *http.Request) {
req.URL.RawQuery = "a[]=xxx&a[]=yyy"
}).
Get("/").
Expect(t).
Status(http.StatusOK).
End()
}
Query Params
There are multiple ways to specify query parameters. These approaches are chainable.
package main
import (
"net/http"
"testing"
"github.com/steinfletcher/apitest"
)
func TestRequests_Query(t *testing.T) {
expectedQueryString := "a=1&a=2&a=9&a=22&b=2"
handler := func(w http.ResponseWriter, r *http.Request) {
if expectedQueryString != r.URL.RawQuery {
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}
apitest.New().
HandlerFunc(handler).
Get("/foo").
Query("a", "9").
Query("a", "22").
QueryCollection(map[string][]string{"a": {"1", "2"}}).
QueryParams(map[string]string{"b": "2"}).
Expect(t).
Status(http.StatusOK).
End()
}
Collection
QueryCollection(map[string][]string{"a": {"1", "2"}})
which results in parameters encoded as a=1&a=2
.
Custom
If the provided approaches are not suitable you can define a request interceptor and implement custom logic.
apitest.New().
Handler(handler).
Intercept(func(req *http.Request) {
req.URL.RawQuery = "a[]=xxx&a[]=yyy"
}).
Get("/path")
Map
QueryParams(map[string]string{"param1": "value1", "param2": "value2"})
Params
Query("param", "value")