How matching works
The order mocks are tried in, the rules for each part of the request, and how to debug a mismatch.
Order
For each outbound request, mocks are tried in the order they were passed to Mocks. The first mock whose matchers all pass answers the request. By default a mock answers one request and is then skipped, so mocks for the same URL answer successive calls in turn:
// the first call fails, the next two succeed
unavailable := apitest.NewMock().
Get("http://flaky/api/status").
RespondWith().
Status(http.StatusServiceUnavailable).
End()
available := apitest.NewMock().
Get("http://flaky/api/status").
RespondWith().
Status(http.StatusOK).
Times(2).
End()
apitest.New().
Mocks(unavailable, available).
Handler(handler).
Get("/").
Expect(t).
Body("503,200,200").
End()Call counts explains Times and AnyTimes.
The rules
| Part | Rule |
|---|---|
| Path | Equal to the mock path, or matched by it as a regular expression. Ignored when the mock has no URL. |
| Host | Equal to the mock host. Ignored when the mock URL has no host. |
| Scheme | Equal to the mock scheme. Ignored when the mock URL has no scheme. |
| Method | Equal to the mock method. |
| Headers, query parameters, form data | Each value is a regular expression matched against the received values. |
| Body | Equal to the mock body as a string, or equivalent JSON. BodyRegexp matches a regular expression. |
| Cookies, basic auth | Equal to the mock's values. |
| Custom matchers | Return nil. |
Every matcher must pass. A matcher whose part of the mock was not set, such as headers on a mock without Header, always passes.
Paths are regular expressions
The mock path is a regular expression, and it is not anchored. Get("http://users/api/user/\\d+") matches /api/user/1234, and also /api/user/1234/avatar:
// matches /api/user/1234 and /api/user/1234/avatar: the expression is not anchored
anyUserPath := apitest.NewMock().
Get(`http://users/api/user/\d+`).
RespondWith().
Status(http.StatusOK).
End()
// $ anchors the end, so /api/user/1234/avatar no longer matches
userOnly := apitest.NewMock().
Get(`http://users/api/user/\d+$`).
RespondWith().
Status(http.StatusOK).
End()
// leave out the scheme and host to anchor the start with ^ as well
anchored := apitest.NewMock().
Get(`^/api/user/\d+$`).
RespondWith().
Status(http.StatusOK).
End()Regular expression characters in a path, such as ., + and ?, also need escaping when you want them matched literally.
Debugging a mismatch
When no mock matches, the client receives an error, and the handler usually responds with an error status. Enable Debug to see why each mock was rejected. In this run the users mock has a typo in its path, and the preferences mock expects a different token:
----------> inbound http request
GET /user/1234 HTTP/1.1
Host: sut
failed to match mocks. Errors: received request did not match any mocks
Mock 1 mismatches:
• received path /api/user/1234 did not match mock path /api/user/12345
Mock 2 mismatches:
• received path /api/user/1234 did not match mock path /api/preferences/1234
• received host users did not match mock host preferences
• not all of received headers map[Authorization:[Bearer s3cr3t]] matched expected mock headers map[Authorization:[Bearer token]]
----------> request to mock
GET /api/user/1234 HTTP/1.1
Host: users
User-Agent: Go-http-client/1.1
Authorization: Bearer s3cr3t
Accept-Encoding: gzip
<---------- response from mock
<---------- final response
HTTP/1.1 502 Bad Gateway
Content-Type: application/json
{"error":"upstream error"}Mocks that have already answered their calls are skipped, so they do not appear in the list.
In a sequence diagram
A sequence diagram shows the same failure at a glance. A call that matched no mock gets no response, so the report marks its arrow with a ✕ and labels the message no response:
// the mock is for user 9999, so the call for user 1234 matches no mock
wrongUser := apitest.NewMock().
Get("http://users/api/user/9999").
RespondWith().
Status(http.StatusOK).
Body(`{"id": "9999", "name": "ola"}`).
End()
apitest.New("reports a call that matched no mock").
Report(apitest.SequenceDiagram()).
Mocks(wrongUser).
Handler(newUserAPI()).
Get("/user/1234").
Expect(t).
Status(http.StatusBadGateway).
End()

ObserveMocks reports every mocked call programmatically, including the ones that did not match.