A simple and extensible behavioral testing library. Supports mocking external http calls and renders sequence diagrams on completion. In behavioral 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.
This project is forked from steinfletcher/apitest apitest was functionally complete. However, I wanted more features, so I decided to fork it to actively develop it further. I will mainly enhance document generation and integration with AWS. There are no plans for compatibility between apitest and spectest. Therefore, future development will include BREAKING CHANGES.
The spectest has its own unique use cases, Use Cases of spectest. Please refer to this document for more information.
- Linux
- Mac
- Windows (Original apitest does not support Windows)
go get -u github.com/nao1215/spectest
Example | Comment |
---|---|
gin | popular martini-like web framework |
graphql | using gqlgen.com to generate a graphql server |
gorilla | the gorilla web toolkit |
iris (broken) | iris web framework |
echo | High performance, extensible, minimalist Go web framework |
fiber | Express inspired web framework written in Go |
httprouter | High performance HTTP request router that scales well |
mocks | example mocking out external http calls |
sequence diagrams | generate sequence diagrams from tests |
Ginkgo | Ginkgo BDD test framework |
plantuml | wxample generating plantuml |
In the original apitest repository, side projects were managed in separate repositories. However, in spectest, these side projects are managed within the same repository. However, the difflib, which operates independently from spectest, and the malfunctioning aws package, are managed in separate repositories.
Library | Comment |
---|---|
JSON Path | JSON Path assertion addons |
JOSN Schema | JSON Schema assertion addons |
CSS Selectors | CSS selector assertion addons |
PlantUML | Export sequence diagrams as plantUML |
DynamoDB (broken) | Add DynamoDB interactions to sequence diagrams |
This library was influenced by the following software packages:
- YatSpec for creating sequence diagrams from tests
- MockMVC and superagent for the concept and behavioral testing approach
- Gock for the approach to mocking HTTP services in Go
- Baloo for API design
func TestApi(t *testing.T) {
spectest.New().
Handler(handler).
Get("/user/1234").
Expect(t).
Body(`{"id": "1234", "name": "Tate"}`).
Status(http.StatusOK).
End()
}
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/nao1215/spectest/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) {
spectest.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) {
spectest.Handler(handler).
Get("/hello").
Expect(t).
Assert(jsonpath.Equal(`$.a`, float64(12345))).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Get("/hello").
Expect(t).
Assert(func(res *http.Response, req *http.Request) error {
assert.Equal(t, http.StatusOK, res.StatusCode)
return nil
}).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Patch("/hello").
Expect(t).
Status(http.StatusOK).
Cookies(spectest.Cookie("ABC").Value("12345")).
CookiePresent("Session-Token").
CookieNotPresent("XXX").
Cookies(
spectest.Cookie("ABC").Value("12345"),
spectest.Cookie("DEF").Value("67890"),
).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Get("/hello").
Expect(t).
Status(http.StatusOK).
Headers(map[string]string{"ABC": "12345"}).
End()
}
var getUser = spectest.NewMock().
Get("/user/12345").
RespondWith().
Body(`{"name": "jon", "id": "1234"}`).
Status(http.StatusOK).
End()
var getPreferences = spectest.NewMock().
Get("/preferences/12345").
RespondWith().
Body(`{"is_contactable": true}`).
Status(http.StatusOK).
End()
func TestApi(t *testing.T) {
spectest.New().
Mocks(getUser, getPreferences).
Handler(handler).
Get("/hello").
Expect(t).
Status(http.StatusOK).
Body(`{"name": "jon", "id": "1234"}`).
End()
}
func TestApi(t *testing.T) {
spectest.New().
Report(spectest.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(spectest.NewSequenceDiagramFormatter(".sequence-diagrams"))
. If you want to change the report file name , you use CustomReportName("file name is here")
. By default, the hash value becomes the report file name.
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.
The spectest checks the Content Type of the response. If it's an image-related MIME type, the image will be displayed in the report. In the apitest, binary data was being displayed.
One feature that does not exist in the apitest fork is the ability to output reports in markdown format. The below code snippet is an example of how to output a markdown report.
func TestApi(t *testing.T) {
spectest.New().
CustomReportName("markdow_report").
Report(spectest.SequenceReport(spectest.ReportFormatterConfig{
Path: "doc",
Kind: spectest.ReportKindMarkdown,
})).
Handler(handler).
Get("/image").
Expect(t).
Body(string(body)).
Header("Content-Type", "image/png").
Header("Content-Length", fmt.Sprint(imageInfo.Size())).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.New().
Debug().
Handler(handler).
Get("/hello").
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Get("/hello").
BasicAuth("username", "password").
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Get("/hello").
WithContext(context.TODO()).
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Get("/hello").
Cookies(spectest.Cookie("ABC").Value("12345")).
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Delete("/hello").
Headers(map[string]string{"My-Header": "12345"}).
Expect(t).
Status(http.StatusOK).
End()
}
Query
, QueryParams
and QueryCollection
can all be used in combination
func TestApi(t *testing.T) {
spectest.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) {
spectest.Handler(handler).
Get("/hello").
QueryCollection(map[string][]string{"a": {"b", "c", "d"}}).
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Post("/hello").
FormData("a", "1").
FormData("b", "2").
FormData("b", "3").
FormData("c", "4", "5", "6").
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.Handler(handler).
Post("/hello").
MultipartFormData("a", "1", "2").
MultipartFile("file", "path/to/some.file1", "path/to/some.file2").
Expect(t).
Status(http.StatusOK).
End()
}
func TestApi(t *testing.T) {
spectest.New().
Observe(func(res *http.Response, req *http.Request, specTest *spectest.SpecTest) {
// do something with res and req
}).
Handler(handler).
Get("/hello").
Expect(t).
Status(http.StatusOK).
End()
}
This is useful for mutating the request before it is sent to the system under test.
func TestApi(t *testing.T) {
spectest.Handler(handler).
Intercept(func(req *http.Request) {
req.URL.RawQuery = "a[]=xxx&a[]=yyy"
}).
Get("/hello").
Expect(t).
Status(http.StatusOK).
End()
}
View the contributing guide.