Skip to content
Permalink
Browse files

Add more test scenarios for mock matchers. Also add more documentatio…

…n around mock matchers.
  • Loading branch information...
enrico5b1b4 committed Aug 2, 2019
1 parent 334bc92 commit a95001a1cb3eb3991728dc217b84a59edddcc7e3
Showing with 164 additions and 3 deletions.
  1. +145 −0 docs/content/docs/mocks.md
  2. +3 −3 mocks.go
  3. +16 −0 mocks_test.go
@@ -43,6 +43,151 @@ apitest.New().

Note that multiple mocks can be defined. Due to FIFO ordering if a request matches more than one mock the first mock matched is used.

## Adding matchers to mocks

You can add matchers for the request headers, cookies, url query parameters and body.

### Header

`Header()` allows you to add a matcher for the header key and value. Regular expressions are also allowed as values.

```go
var getUserMock = apitest.NewMock().
Get("http://example.com/user/12345").
Header("foo", "bar").
Header("token", "b([a-z]+)z").
Headers(map[string]string{"name": "John"})
RespondWith().
Body(`{"name": "jon"}`).
Status(http.StatusOK).
End()
```

You can also require a header to be present (`HeaderPresent()`) or not present (`HeaderNotPresent()`)

```go
var getUserMock = apitest.NewMock().
Get("http://example.com/user/12345").
HeaderPresent("authtoken").
HeaderNotPresent("requestid").
RespondWith().
Body(`{"name": "jon"}`).
Status(http.StatusOK).
End()
```

### Query Parameters

`Query()` allows you to add a matcher for the a url query parameter key and value. Regular expressions are also allowed as values.

```go
var getUserMock = apitest.NewMock().
Get("http://example.com/user/12345").
Query("page", "1").
Query("name", "Jo([a-z]+)n").
QueryParams(map[string]string{"orderBy": "ASC"}).
RespondWith().
Body(`{"name": "jon"}`).
Status(http.StatusOK).
End()
```

You can also require a query parameter to be present (`QueryPresent()`) or not present (`QueryNotPresent()`)

```go
var getUserMock = apitest.NewMock().
Get("http://example.com/user/12345").
QueryPresent("page").
QueryNotPresent("name").
RespondWith().
Body(`{"name": "jon"}`).
Status(http.StatusOK).
End()
```

### Cookies

`Cookie()` allows you to add a matcher for a cookie name and value.

```go
var getUserMock = apitest.NewMock().
Get("http://example.com/user/12345").
Cookie("sessionid", "1321").
RespondWith().
Body(`{"name": "jon"}`).
Status(http.StatusOK).
End()
```

You can also require a cookie name to be present (`CookiePresent()`) or not present (`CookieNotPresent()`)

```go
var getUserMock = apitest.NewMock().
Get("http://example.com/user/12345").
CookiePresent("trackingid").
CookieNotPresent("analytics").
RespondWith().
Body(`{"name": "jon"}`).
Status(http.StatusOK).
End()
```

### Body

`Body()` allows you to add a matcher for the body of the request.

```go
var getUserMock = apitest.NewMock().
Post("http://example.com/user/12345").
Body(`{"username": "John"}`).
RespondWith().
Status(http.StatusOK).
End()
```

If you are working with a URL encoded form body, you can use `FormData()` to match a key and value. Regular expressions are also allowed as values.

```go
var getUserMock = apitest.NewMock().
Post("http://example.com/user/12345").
FormData("name", "Simon").
FormData("name", "Jo([a-z]+)n").
RespondWith().
Status(http.StatusOK).
End()
```

You can also require a form body key to be present (`FormDataPresent()`) or not present (`FormDataNotPresent()`)

```go
var getUserMock = apitest.NewMock().
Post("http://example.com/user/12345").
FormDataPresent("name").
FormDataNotPresent("pets").
RespondWith().
Status(http.StatusOK).
End()
```

### Custom matcher

You can write you own custom matcher using `AddMatcher()`.
A matcher function is defined as `func(*http.Request, *MockRequest) error`

```go
var getUserMock = apitest.NewMock().
Post("http://example.com/user/12345").
AddMatcher(func(req *http.Request, mockReq *MockRequest) error {
if req.Method == http.MethodPost {
return nil
}
return errors.New("invalid http method")
}).
RespondWith().
Status(http.StatusOK).
End()
```

## Standalone Mode

You can use mocks outside of API tests by using the `EndStandalone` termination method on the mock builder. This is useful for testing http clients outside of api tests.
@@ -627,7 +627,7 @@ var headerMatcher = func(req *http.Request, spec *MockRequest) error {
for _, value := range values {
match, err = regexp.MatchString(value, field)
if err != nil {
return fmt.Errorf("unable to match received header value %s against expected value %s", value, field)
return fmt.Errorf("failed to parse regexp for header %s with value %s", key, value)
}
}

@@ -675,7 +675,7 @@ var queryParamMatcher = func(req *http.Request, spec *MockRequest) error {
for _, value := range values {
match, err := regexp.MatchString(value, field)
if err != nil {
return fmt.Errorf("unable to match received query param value %s against expected value %s", value, field)
return fmt.Errorf("failed to parse regexp for query param %s with value %s", key, value)
}

if match {
@@ -731,7 +731,7 @@ var formDataMatcher = func(req *http.Request, spec *MockRequest) error {
for _, value := range values {
match, err := regexp.MatchString(value, field)
if err != nil {
return fmt.Errorf("unable to match received form data values %s against expected mock form data values %s", value, field)
return fmt.Errorf("failed to parse regexp for form data %s with value %s", key, value)
}

if match {
@@ -161,6 +161,8 @@ func TestMocks_HeaderMatcher(t *testing.T) {
{map[string]string{"B": "5", "A": "123"}, "A", "123", nil},
{map[string]string{"A": "123"}, "C", "3", errors.New("not all of received headers map[A:[123]] matched expected mock headers map[C:[3]]")},
{map[string]string{}, "", "", nil},
{map[string]string{"A": "apple"}, "A", "a([a-z]+)ple", nil},
{map[string]string{"A": "apple"}, "A", "a-z]+)ch_invalid_regexp", errors.New("failed to parse regexp for header A with value a-z]+)ch_invalid_regexp")},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%s %s", test.headerToMatchKey, test.headerToMatchValue), func(t *testing.T) {
@@ -249,6 +251,7 @@ func TestMocks_QueryMatcher_Success(t *testing.T) {
{"http://test.com/v2/path?b=2&a=1", map[string][]string{"b": {"2"}, "a": {"1"}}},
{"http://test.com/v2/path?b=2&a=1&a=2", map[string][]string{"a": {"2"}}},
{"http://test.com/v2/path?b=2&a=1&a=2", map[string][]string{"a": {"2", "1"}}},
{"http://test.com/v2/path?b=2&a=apple", map[string][]string{"a": {"a([a-z]+)ple"}}},
}
for _, test := range tests {
t.Run(test.requestUrl, func(t *testing.T) {
@@ -276,6 +279,7 @@ func TestMocks_QueryMatcher_Errors(t *testing.T) {
{"http://test.com/v2/path?a=1", map[string][]string{"b": {"1"}}, errors.New("not all of received query params map[a:[1]] matched expected mock query params map[b:[1]]")},
{"http://test.com/v2/path?b=2&a=1&a=2&a=3", map[string][]string{"a": {"4", "1", "2"}}, errors.New("b:[2]")},
{"http://test.com/v2/path?b=2&a=1&a=2&a=3", map[string][]string{"a": {"4", "1", "2"}}, errors.New("a:[1 2 3]")},
{"http://test.com/v2/path?b=2&a=1", map[string][]string{"a": {"a-z]+)ch_invalid_regexp"}}, errors.New("failed to parse regexp for query param a with value a-z]+)ch_invalid_regexp")},
}
for _, test := range tests {
t.Run(test.requestUrl, func(t *testing.T) {
@@ -361,6 +365,12 @@ func TestMocks_FormDataMatcher(t *testing.T) {
map[string][]string{"a": {"1"}},
nil,
},
{
"single key match with regular expression",
map[string][]string{"a": {"apple"}},
map[string][]string{"a": {"a([a-z]+)ple"}},
nil,
},
{
"multiple key match",
map[string][]string{"a": {"1"}, "b": {"1"}},
@@ -397,6 +407,12 @@ func TestMocks_FormDataMatcher(t *testing.T) {
map[string][]string{"a": {"1", "3", "4"}},
errors.New("not all of received form data values map[a:[1 2 4]] matched expected mock form data values map[a:[1 3 4]]"),
},
{
"error when regular expression provided is invalid",
map[string][]string{"a": {"1"}},
map[string][]string{"a": {"a-z]+)ch_invalid_regexp"}},
errors.New("failed to parse regexp for form data a with value a-z]+)ch_invalid_regexp"),
},
}

for _, test := range tests {

0 comments on commit a95001a

Please sign in to comment.
You can’t perform that action at this time.