Categorygithub.com/api7/apitest
modulepackage
1.4.9
Repository: https://github.com/api7/apitest.git
Documentation: pkg.go.dev

# README

Godoc Coverage Status Build Status Go Report Card

apitest

A simple and extensible behavioural testing library. Supports mocking external http calls and renders sequence diagrams on completion.

In behavioural tests the internal structure of the app is not known by the tests. Data is input to the system and the outputs are expected to meet certain conditions.

Join the conversation at #apitest on https://gophers.slack.com.

Logo by @egonelbre

Documentation

Please visit https://apitest.dev for the latest documentation.

Installation

go get -u github.com/steinfletcher/apitest

Examples

Framework and library integration examples

ExampleComment
ginpopular martini-like web framework
gorillathe gorilla web toolkit
irisiris web framework
echoHigh performance, extensible, minimalist Go web framework
fiberExpress inspired web framework written in Go
httprouterHigh performance HTTP request router that scales well
mocksexample mocking out external http calls
sequence diagramsgenerate sequence diagrams from tests. See the demo

Companion libraries

LibraryComment
JSONPathJSONPath assertion addons
CSS SelectorsCSS selector assertion addons
PlantUMLExport sequence diagrams as plantUML
DynamoDBAdd DynamoDB interactions to sequence diagrams

Credits

This library was influenced by the following software packages:

  • YatSpec for creating sequence diagrams from tests
  • MockMVC and superagent for the concept and behavioural testing approach
  • Gock for the approach to mocking HTTP services in Go
  • Baloo for API design

Credit to testify which is this libraries' only dependency.

Code snippets

JSON body matcher

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/user/1234").
		Expect(t).
		Body(`{"id": "1234", "name": "Tate"}`).
		Status(http.StatusCreated).
		End()
}

JSONPath

For asserting on parts of the response body JSONPath may be used. A separate module must be installed which provides these assertions - go get -u github.com/steinfletcher/apitest-jsonpath. This is packaged separately to keep this library dependency free.

Given the response is {"a": 12345, "b": [{"key": "c", "value": "result"}]}

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		Expect(t).
		Assert(jsonpath.Contains(`$.b[? @.key=="c"].value`, "result")).
		End()
}

and jsonpath.Equals checks for value equality

func TestApi(t *testing.T) {
	apitest.New(handler).
		Get("/hello").
		Expect(t).
		Assert(jsonpath.Equal(`$.a`, float64(12345))).
		End()
}

Custom assert functions

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		Expect(t).
		Assert(func(res *http.Response, req *http.Request) {
			assert.Equal(t, http.StatusOK, res.StatusCode)
		}).
		End()
}

Assert cookies

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Patch("/hello").
		Expect(t).
		Status(http.StatusOK).
		Cookies(apitest.Cookie"ABC").Value("12345")).
		CookiePresent("Session-Token").
		CookieNotPresent("XXX").
		Cookies(
			apitest.Cookie("ABC").Value("12345"),
			apitest.Cookie("DEF").Value("67890"),
		).
		End()
}

Assert headers

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		Expect(t).
		Status(http.StatusOK).
		Headers(map[string]string{"ABC": "12345"}).
		End()
}

Mocking external http calls

var getUser = apitest.NewMock().
	Get("/user/12345").
	RespondWith().
	Body(`{"name": "jon", "id": "1234"}`).
	Status(http.StatusOK).
	End()

var getPreferences = apitest.NewMock().
	Get("/preferences/12345").
	RespondWith().
	Body(`{"is_contactable": true}`).
	Status(http.StatusOK).
	End()

func TestApi(t *testing.T) {
	apitest.New().
		Mocks(getUser, getPreferences).
		Handler(handler).
		Get("/hello").
		Expect(t).
		Status(http.StatusOK).
		Body(`{"name": "jon", "id": "1234"}`).
		End()
}

Generating sequence diagrams from tests


func TestApi(t *testing.T) {
	apitest.New().
		Report(apitest.SequenceDiagram()).
		Mocks(getUser, getPreferences).
		Handler(handler).
		Get("/hello").
		Expect(t).
		Status(http.StatusOK).
		Body(`{"name": "jon", "id": "1234"}`).
		End()
}

It is possible to override the default storage location by passing the formatter instance Report(apitest.NewSequenceDiagramFormatter(".sequence-diagrams")). You can bring your own formatter too if you want to produce custom output. By default a sequence diagram is rendered on a html page. See the demo

Debugging http requests and responses generated by api test and any mocks

func TestApi(t *testing.T) {
	apitest.New().
		Debug().
		Handler(handler).
		Get("/hello").
		Expect(t).
		Status(http.StatusOK).
		End()
}

Provide basic auth in the request

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		BasicAuth("username", "password").
		Expect(t).
		Status(http.StatusOK).
		End()
}

Provide cookies in the request

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		Cookies(apitest.Cookie("ABC").Value("12345")).
		Expect(t).
		Status(http.StatusOK).
		End()
}

Provide headers in the request

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Delete("/hello").
		Headers(map[string]string{"My-Header": "12345"}).
		Expect(t).
		Status(http.StatusOK).
		End()
}

Provide query parameters in the request

Query, QueryParams and QueryCollection can all be used in combination

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		QueryParams(map[string]string{"a": "1", "b": "2"}).
		Query("c", "d").
		Expect(t).
		Status(http.StatusOK).
		End()
}

Providing {"a": {"b", "c", "d"} results in parameters encoded as a=b&a=c&a=d. QueryCollection can be used in combination with Query

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Get("/hello").
		QueryCollection(map[string][]string{"a": {"b", "c", "d"}}).
		Expect(t).
		Status(http.StatusOK).
		End()
}

Provide a url encoded form body in the request

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Post("/hello").
		FormData("a", "1").
		FormData("b", "2").
		FormData("b", "3").
		FormData("c", "4", "5", "6").
		Expect(t).
		Status(http.StatusOK).
		End()
}

Capture the request and response data

func TestApi(t *testing.T) {
	apitest.New().
		Observe(func(res *http.Response, req *http.Request, apiTest *apitest.APITest) {
			// do something with res and req
		}).
		Handler(handler).
		Get("/hello").
		Expect(t).
		Status(http.StatusOK).
		End()
}

Intercept the request

This is useful for mutating the request before it is sent to the system under test.

func TestApi(t *testing.T) {
	apitest.New().
		Handler(handler).
		Intercept(func(req *http.Request) {
			req.URL.RawQuery = "a[]=xxx&a[]=yyy"
		}).
		Get("/hello").
		Expect(t).
		Status(http.StatusOK).
		End()
}

Contributing

View the contributing guide.

# Packages

No description provided by the author
No description provided by the author

# Functions

FromHTTPCookie transforms an http cookie into a Cookie.
New creates a new api test.
NewCookie creates a new Cookie with the provided name.
NewMock create a new mock, ready for configuration using the builder pattern.
NewStandaloneMocks create a series of StandaloneMocks.
NewTestRecorder creates a new TestRecorder.
SequenceDiagram produce a sequence diagram at the given path or .sequence by default.

# Constants

ConsumerName default consumer name.
SystemUnderTestDefaultName default name for system under test.

# Variables

IsClientError is a convenience function to assert on a range of client error status codes.
IsServerError is a convenience function to assert on a range of server error status codes.
IsSuccess is a convenience function to assert on a range of happy path status codes.

# Structs

APITest is the top level struct holding the test spec.
Cookie used to represent an http cookie.
FinalResponse used to wrap the final response with a timestamp.
No description provided by the author
No description provided by the author
InboundRequest used to wrap the incoming request with a timestamp.
No description provided by the author
No description provided by the author
Mock represents the entire interaction for a mock to be used for testing.
MockRequest represents the http request side of a mock interaction.
MockResponse represents the http response side of a mock interaction.
NoopVerifier is a verifier that does not perform verification.
No description provided by the author
Request is the user defined request that will be invoked on the handler under test.
Response is the user defined expected response from the application under test.
Result provides the final result.
No description provided by the author
StandaloneMocks for using mocks outside of API tests context.
Transport wraps components used to observe and manipulate the real request and response objects.

# Interfaces

No description provided by the author
No description provided by the author
Verifier is the assertion interface allowing consumers to inject a custom assertion implementation.

# Type aliases

Assert is a user defined custom assertion function.
Intercept will be called before the request is made.
Matcher type accepts the actual request and a mock request to match against.
Observe will be called by with the request and response on completion.
RecorderHook used to implement a custom interaction recorder.