httpspec

package
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 25, 2020 License: Apache-2.0 Imports: 11 Imported by: 0

README

Table of Contents generated with DocToc

httpspec

httpspec allow you to create HTTP API specifications with ease.

Documentation

The documentation maintained in GoDoc, including the examples.

Usage

package mypkg_test

import (
	"encoding/json"
	"net/http"
	"testing"

	"my/pkg/path/mypkg"

	"github.com/stretchr/testify/require"

	"github.com/adamluzsi/testcase"
	. "github.com/adamluzsi/testcase/httpspec"
)

func TestMyHandlerCreate(t *testing.T) {
	s := testcase.NewSpec(t)

	GivenThisIsAJSONAPI(s)

	// Arrange
	LetHandler(s, func(t *testcase.T) http.Handler { return mypkg.MyHandler{} })
	LetMethodValue(s, http.MethodPost)
	LetPathValue(s, `/`)
	LetBody(s, func(t *testcase.T) interface{} {
		// this will end up as {"foo":"bar"} in the request body
		return map[string]string{"foo": "bar"}
	})

	s.Then(`it will...`, func(t *testcase.T) {
		rr := ServeHTTP(t) // Act
		require.Equal(t, http.StatusOK, rr.Code)
		var resp mypkg.CreateResponse
		require.Nil(t, json.Unmarshal(rr.Body.Bytes(), &resp))
		// more assertion
	})
}
package mypkg_test

import (
	"encoding/json"
	"net/http"
	"testing"

	"my/pkg/path/mypkg"

	"github.com/stretchr/testify/require"

	"github.com/adamluzsi/testcase"
	. "github.com/adamluzsi/testcase/httpspec"
)

func TestMyHandler(t *testing.T) {
	s := testcase.NewSpec(t)

	GivenThisIsAJSONAPI(s)

	LetHandler(s, func(t *testcase.T) http.Handler { return mypkg.MyHandler{} })

	s.Describe(`POST / - create X`, func(s *testcase.Spec) {
		LetMethodValue(s, http.MethodPost)
		LetPathValue(s, `/`)

		LetBody(s, func(t *testcase.T) interface{} {
			// this will end up as {"foo":"bar"} in the request body
			return map[string]string{"foo": "bar"}
		})

		var onSuccess = func(t *testcase.T) mypkg.CreateResponse {
			rr := ServeHTTP(t)
			require.Equal(t, http.StatusOK, rr.Code)
			var resp mypkg.CreateResponse
			require.Nil(t, json.Unmarshal(rr.Body.Bytes(), &resp))
			return resp
		}

		s.Then(`it will create a new resource`, func(t *testcase.T) {
			createResponse := onSuccess(t)
			// assert
			_ = createResponse
		})
	})
}

Documentation

Index

Examples

Constants

View Source
const (
	RequestContextVarName = requestAttributePrefix + `context`
	RequestMethodVarName  = requestAttributePrefix + `method`
	RequestPathVarName    = requestAttributePrefix + `path`
	RequestQueryVarName   = requestAttributePrefix + `query`
	RequestHeaderVarName  = requestAttributePrefix + `header`
	RequestBodyVarName    = requestAttributePrefix + `body`
	RequestHandlerVarName = requestAttributePrefix + `handler`
)

Variables

View Source
var Debug bool

Debug enables debug logs for httpspec

Functions

func GivenThisIsAJSONAPI

func GivenThisIsAJSONAPI(s *testcase.Spec)
Example
s := testcase.NewSpec(testingT)

GivenThisIsAJSONAPI(s)

LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

s.Describe(`POST / - create X`, func(s *testcase.Spec) {
	LetMethodValue(s, http.MethodPost)
	LetPathValue(s, `/`)

	LetBody(s, func(t *testcase.T) interface{} {
		// this will end up as {"foo":"bar"} in the request body
		return map[string]string{"foo": "bar"}
	})

	var onSuccess = func(t *testcase.T) CreateResponse {
		rr := ServeHTTP(t)
		require.Equal(t, http.StatusOK, rr.Code)
		var resp CreateResponse
		require.Nil(t, json.Unmarshal(rr.Body.Bytes(), &resp))
		return resp
	}

	s.Then(`it will create a new resource`, func(t *testcase.T) {
		createResponse := onSuccess(t)
		// assert
		_ = createResponse
	})
})
Output:

func GivenThisIsAnAPI

func GivenThisIsAnAPI(s *testcase.Spec)
Example
s := testcase.NewSpec(testingT)

GivenThisIsAnAPI(s)
LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

s.Before(func(t *testcase.T) {
	t.Log(`given authentication header is set`)
	Header(t).Set(`X-Auth-Token`, `token`)
})

s.Describe(`GET / - list of X`, func(s *testcase.Spec) {
	LetMethodValue(s, http.MethodGet)
	LetPathValue(s, `/`)

	var onSuccess = func(t *testcase.T) ListResponse {
		rr := ServeHTTP(t)
		require.Equal(t, http.StatusOK, rr.Code)
		// unmarshal the response from rr.body
		return ListResponse{}
	}

	s.And(`something is set in the query`, func(s *testcase.Spec) {
		s.Before(func(t *testcase.T) {
			Query(t).Set(`something`, `value`)
		})

		s.Then(`it will react to it as`, func(t *testcase.T) {
			listResponse := onSuccess(t)
			// assert
			_ = listResponse
		})
	})

	s.Then(`it will return the list of resource`, func(t *testcase.T) {
		listResponse := onSuccess(t)
		// assert
		_ = listResponse
	})
})

s.Describe(`GET /{resourceID} - show X`, func(s *testcase.Spec) {
	LetMethodValue(s, http.MethodGet)
	LetPath(s, func(t *testcase.T) string {
		return fmt.Sprintf(`/%s`, t.I(`resourceID`))
	})

	var onSuccess = func(t *testcase.T) ShowResponse {
		rr := ServeHTTP(t)
		require.Equal(t, http.StatusOK, rr.Code)
		// unmarshal the response from rr.body
		return ShowResponse{}
	}

	s.Then(`it will return the resource 'show'' representation`, func(t *testcase.T) {
		showResponse := onSuccess(t)
		// assert
		_ = showResponse
	})
})
Output:

func Header(t *testcase.T) http.Header

Header allows you to set the current test scope's http path for ServeHTTP.

Example
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)
httpspec.LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

s.Before(func(t *testcase.T) {
	// this is ideal to represent query string inputs
	httpspec.Header(t).Set(`Foo`, `bar`)
})

s.Test(`the *http.Request URL Query will have 'Foo: bar'`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

func LetBody

func LetBody(s *testcase.Spec, f func(t *testcase.T) interface{})

LetBody allow you to define a http request body value for the ServeHTTP. The value of this can be a struct, map or url.Values. The Serialization for the request body is based on the Header "Content-Type" value. Currently only json and form url encoding is supported.

Example
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)
httpspec.LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

httpspec.LetBody(s, func(t *testcase.T) interface{} {
	return map[string]string{"hello": "world"}
})

s.Before(func(t *testcase.T) {
	// this set the content-type for json, so json marshal will be used.
	httpspec.Header(t).Set(`Content-Type`, `application/json`)
})

s.Test(`the *http.Request body io.Reader will have the encoded body`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

func LetContext

func LetContext(s *testcase.Spec, f func(t *testcase.T) context.Context)

LetContext allow you to Set the ServeHTTP request context

Example
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)
httpspec.LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

httpspec.LetContext(s, func(t *testcase.T) context.Context {
	return context.Background()
})

s.Test(`the *http.Request#Context() will have foo-bar`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

Example (WithValue)
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)
httpspec.LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

s.Before(func(t *testcase.T) {
	// this is ideal for representing middleware prerequisite
	// in the form of a value in the context that is guaranteed by a middleware.
	// Use this only if you cannot make it part of the specification level context value deceleration with LetContext.
	ctx := t.I(httpspec.RequestContextVarName).(context.Context)
	ctx = context.WithValue(ctx, `foo`, `bar`)
	t.Let(httpspec.RequestContextVarName, ctx)
})

s.Test(`the *http.Request#Context() will have foo-bar`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

func LetHandler

func LetHandler(s *testcase.Spec, f func(t *testcase.T) http.Handler)

LetHandler is the subject of a HTTP Spec. You must set for all the spec

func LetMethod

func LetMethod(s *testcase.Spec, f func(t *testcase.T) string)

LetMethod allow you to set the current test scope's http method for ServeHTTP

Example
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)

httpspec.LetMethod(s, func(t *testcase.T) string {
	// set the HTTP Method to get for the *http.Request
	return http.MethodGet
})

s.Test(`GET /`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

func LetMethodValue

func LetMethodValue(s *testcase.Spec, m string)

LetMethodValue allow you to set the current test scope's http method for ServeHTTP

Example
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)

// set the HTTP Method to get for the *http.Request
httpspec.LetMethodValue(s, http.MethodGet)

s.Test(`GET /`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

func LetPath

func LetPath(s *testcase.Spec, f func(t *testcase.T) string)

LetPath allows you to set the current test scope's http path for ServeHTTP.

func LetPathValue

func LetPathValue(s *testcase.Spec, p string)

LetPathValue allows you to set the current test scope's http path for ServeHTTP.

func Query

func Query(t *testcase.T) url.Values

Query allows you to retrieve the current test scope's http path query that will be used for ServeHTTP. In a Before Block you can access the query and then specify the values in it.

Example
s := testcase.NewSpec(testingT)

httpspec.GivenThisIsAnAPI(s)
httpspec.LetHandler(s, func(t *testcase.T) http.Handler { return MyHandler{} })

s.Before(func(t *testcase.T) {
	// this is ideal to represent query string inputs
	httpspec.Query(t).Set(`foo`, `bar`)
})

s.Test(`the *http.Request URL Query will have foo=bar`, func(t *testcase.T) {
	httpspec.ServeHTTP(t)
})
Output:

func ServeHTTP

func ServeHTTP(t *testcase.T) *httptest.ResponseRecorder

ServeHTTP will make a request to the spec context it requires the following spec variables

  • method -> http method <string>
  • path -> http path <string>
  • query -> http query string <url.Values>
  • body -> http payload <io.Reader|io.ReadCloser>

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL