Class MockRestServer
- All Implemented Interfaces:
AutoCloseable
@RestClient interface, without depending on a real network - point
RIP.getClient(Class, String) at
baseUrl() and this server stands in for the real one.
Deliberately a genuine HTTP server (the same com.sun.net.httpserver
this project's own integration test suite already uses), listening on
loopback, rather than a fake transport swapped in underneath Unirest - so
every request this server receives went through RIP's real request-building
and Unirest's real serialization, exactly as it would against a real
server. @Retry, @Timeout, and every registered
RequestInterceptor all run completely unmodified.
MockRestServer server = MockRestServer.start();
server.on(HTTPMethod.GET, "/orders/{id}", MockResponse.ok("{\"status\":\"CONFIRMED\"}"));
OrderApi api = RIP.getClient(OrderApi.class, server.baseUrl());
Order order = api.getOrder("abc123");
RecordedRequest sent = server.takeRequest();
assertEquals("/orders/abc123", sent.getPath());
server.close();
-
Method Summary
Modifier and TypeMethodDescriptionbaseUrl()Returns this server's base URL, to pass toRIP.getClient(Class, String)orRipClientConfig.Builder.baseUrl(String).voidclose()Stops the server.intcountOf(HTTPMethod httpMethod, String pathTemplate) Returns how many recorded requests matchhttpMethodandpathTemplate- a check for "was this endpoint called, and how many times" without manually filteringgetRecordedRequests()or looping overtakeRequest().enqueue(MockResponse response) Queues a response for the next request that doesn't match any route registered viaon(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)- a route always wins over this queue for a request it matches, so this only ever answers a path with no matching route at all.enqueueFor(HTTPMethod httpMethod, String pathTemplate, MockResponse response) Scripts a one-time response for a route already registered viaon(HTTPMethod, String, MockResponse), consumed - in the order queued - before that route's sticky response, the way to combine a scripted failure sequence with a sticky final answer for the same path (e.g. a503then a200, to prove@Retryrecovers, while every request after that still gets the200the route was registered with).enqueueFor(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams, MockResponse response) Same asenqueueFor(HTTPMethod, String, MockResponse), for a route registered viaon(HTTPMethod, String, Map, MockResponse)with the samerequiredQueryParams.Returns every request recorded so far, oldest first, without consuming them - unliketakeRequest().Returns every registered route that hasn't matched any recorded request yet, as"METHOD path"strings (e.g.on(HTTPMethod httpMethod, String pathTemplate, MockResponse response) Registers a response for every request matchinghttpMethodandpathTemplate- unlikeenqueue(com.shri.restinpeace.mock.MockResponse), this doesn't get consumed; it keeps answering every matching request the same way.on(HTTPMethod httpMethod, String pathTemplate, Predicate<RecordedRequest> matcher, MockResponse response) Same ason(HTTPMethod, String, MockResponse), but only matches a request for whichmatcheralso returnstrue- for matching on a request header or the request body, neither of which therequiredQueryParamsoverload covers, e.g.on(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams, MockResponse response) Same ason(HTTPMethod, String, MockResponse), but only matches a request whose query params contain every entry inrequiredQueryParamswith an equal value - any other query params the request carries are ignored.onFlaky(HTTPMethod httpMethod, String pathTemplate, int failuresBeforeSuccess, MockResponse failureResponse, MockResponse successResponse) Registers a route that failsfailuresBeforeSuccesstimes withfailureResponsebefore settling intosuccessResponsefor every request after that - sugar for callingon(HTTPMethod, String, MockResponse)withsuccessResponseand thenenqueueFor(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)withfailureResponsefailuresBeforeSuccesstimes.onPages(HTTPMethod httpMethod, String pathTemplate, MockResponse... responses) Scripts an ordered sequence of responses for a route - the 1st matching request getsresponses[0], the 2nd getsresponses[1], and so on, with the last response answering every request after the sequence is exhausted.onPages(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams, MockResponse... responses) Same asonPages(HTTPMethod, String, MockResponse...), but only matches a request whose query params contain every entry inrequiredQueryParams- for a path also serving other, non-paginated traffic that a bareonPages(HTTPMethod, String, MockResponse...)route would otherwise intercept too.booleanremove(HTTPMethod httpMethod, String pathTemplate) Removes a route registered viaon(HTTPMethod, String, MockResponse), so a later request to that path falls through to any other registered route, theenqueue(com.shri.restinpeace.mock.MockResponse)queue, or the default "no response was queued or registered" failure - without a fullreset(), which would also wipe every other route, the queue, and recorded-request history.booleanSame asremove(HTTPMethod, String), for a route registered viaon(HTTPMethod, String, Map, MockResponse)with the samerequiredQueryParams.intReturns how many requests have been recorded so far.reset()Clears every queued response, registered route, and recorded request, so the same server can be reused across multiple tests instead of paying to start a new one for each - e.g. from a@BeforeEachmethod, with the server itself started once in a@BeforeAll.static MockRestServerstart()Starts a new server on an OS-assigned loopback port.Removes and returns the oldest recorded request, for asserting against one call at a time in the order calls were made.
-
Method Details
-
start
Starts a new server on an OS-assigned loopback port.- Returns:
- the started server
-
baseUrl
Returns this server's base URL, to pass toRIP.getClient(Class, String)orRipClientConfig.Builder.baseUrl(String).- Returns:
- the base URL, e.g.
"http://localhost:54321"
-
enqueue
Queues a response for the next request that doesn't match any route registered viaon(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)- a route always wins over this queue for a request it matches, so this only ever answers a path with no matching route at all. To script a sequence of responses to a path that also has a sticky final answer (e.g. a503then a200, to prove@Retryrecovers, while still returning200for every request after that), register the route withon(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)first and useenqueueFor(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)instead - this method can't do that for a path any route already covers.- Parameters:
response- the response to queue- Returns:
- this server
-
on
Registers a response for every request matchinghttpMethodandpathTemplate- unlikeenqueue(com.shri.restinpeace.mock.MockResponse), this doesn't get consumed; it keeps answering every matching request the same way.pathTemplatemay contain{name}placeholders (matching RIP's own path-param syntax), each matching exactly one path segment, e.g."/orders/{id}"matches/orders/abc123but not/orders/abc/123. Checked before theenqueue(com.shri.restinpeace.mock.MockResponse)queue, in the order routes were registered - the first matching route wins. Calling this again for the samehttpMethodandpathTemplatereplaces that route's response in place (keeping its position relative to other routes) instead of adding a second, permanently-shadowed registration - the way to change one route's behavior mid-test.- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholdersresponse- the response to send for every matching request- Returns:
- this server
-
on
public MockRestServer on(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams, MockResponse response) Same ason(HTTPMethod, String, MockResponse), but only matches a request whose query params contain every entry inrequiredQueryParamswith an equal value - any other query params the request carries are ignored. Useful for an endpoint that behaves differently depending on a query param, e.g. registering one route for?status=activeand another for?status=archivedon the same path. As with the simpler overload, routes are checked in registration order and the first match wins - register the more specific (query-constrained) route first if a less specific one for the same path would otherwise shadow it. Calling this again with the samehttpMethod,pathTemplate, andrequiredQueryParamsreplaces that route in place, the same as the simpler overload.- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholdersrequiredQueryParams- the query params that must be present with these exact values for this route to matchresponse- the response to send for every matching request- Returns:
- this server
-
on
public MockRestServer on(HTTPMethod httpMethod, String pathTemplate, Predicate<RecordedRequest> matcher, MockResponse response) Same ason(HTTPMethod, String, MockResponse), but only matches a request for whichmatcheralso returnstrue- for matching on a request header or the request body, neither of which therequiredQueryParamsoverload covers, e.g.request -> "v2".equals(request.getHeader("X-Api-Version"))orrequest -> request.getBody().contains("\"tier\":\"premium\""). As with the other overloads, routes are checked in registration order and the first match wins - register the more specific (matcher) route first if a less specific one for the same path would otherwise shadow it.Unlike the other
on(...)overloads, calling this again never replaces an earlier registration - two arbitraryPredicates can't be compared for equality the way arequiredQueryParamsmap can, so there's no reliable way to tell whether it's "the same" route being re-registered. Useremove(com.shri.restinpeace.constant.HTTPMethod, java.lang.String)first if that's the intent.- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholdersmatcher- an additional condition the request must satisfyresponse- the response to send for every matching request- Returns:
- this server
-
enqueueFor
Scripts a one-time response for a route already registered viaon(HTTPMethod, String, MockResponse), consumed - in the order queued - before that route's sticky response, the way to combine a scripted failure sequence with a sticky final answer for the same path (e.g. a503then a200, to prove@Retryrecovers, while every request after that still gets the200the route was registered with). Unlikeenqueue(com.shri.restinpeace.mock.MockResponse), which only ever answers a path with no route at all, this attaches to a specific route regardless of what other routes are registered.- Parameters:
httpMethod- the HTTP method of the route to script a response forpathTemplate- the path template of the route, exactly as passed toon(HTTPMethod, String, MockResponse)response- the one-time response to queue- Returns:
- this server
- Throws:
NoSuchElementException- if no route matchinghttpMethodandpathTemplatehas been registered viaon(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)yet
-
enqueueFor
public MockRestServer enqueueFor(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams, MockResponse response) Same asenqueueFor(HTTPMethod, String, MockResponse), for a route registered viaon(HTTPMethod, String, Map, MockResponse)with the samerequiredQueryParams.- Parameters:
httpMethod- the HTTP method of the route to script a response forpathTemplate- the path template of the route, exactly as passed toon(HTTPMethod, String, Map, MockResponse)requiredQueryParams- the required query params of the route, exactly as passed toon(HTTPMethod, String, Map, MockResponse)response- the one-time response to queue- Returns:
- this server
- Throws:
NoSuchElementException- if no matching route has been registered viaon(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)yet
-
onFlaky
public MockRestServer onFlaky(HTTPMethod httpMethod, String pathTemplate, int failuresBeforeSuccess, MockResponse failureResponse, MockResponse successResponse) Registers a route that failsfailuresBeforeSuccesstimes withfailureResponsebefore settling intosuccessResponsefor every request after that - sugar for callingon(HTTPMethod, String, MockResponse)withsuccessResponseand thenenqueueFor(com.shri.restinpeace.constant.HTTPMethod, java.lang.String, com.shri.restinpeace.mock.MockResponse)withfailureResponsefailuresBeforeSuccesstimes.- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholdersfailuresBeforeSuccess- how many requests getfailureResponsebeforesuccessResponsetakes overfailureResponse- the response sent for the firstfailuresBeforeSuccessmatching requestssuccessResponse- the response sent for every matching request after that- Returns:
- this server
-
onPages
public MockRestServer onPages(HTTPMethod httpMethod, String pathTemplate, MockResponse... responses) Scripts an ordered sequence of responses for a route - the 1st matching request getsresponses[0], the 2nd getsresponses[1], and so on, with the last response answering every request after the sequence is exhausted. Sugar foron(HTTPMethod, String, MockResponse)with the last response andenqueueFor(HTTPMethod, String, MockResponse)with the rest, in the right order - the natural way to script a@Paginated/PaginationStrategy<T>fetch's page-by-page response sequence in one call, without needing a separate route per page distinguished by cursor/offset query param value (seedocs/design/pagination-helper.mdยง11).server.onPages(HTTPMethod.GET, "/orders", MockResponse.ok("{\"orders\":[{\"id\":\"1\"}],\"next\":true}"), MockResponse.ok("{\"orders\":[{\"id\":\"2\"}],\"next\":false}"));- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholdersresponses- the page sequence, in order; must not be empty- Returns:
- this server
- Throws:
IllegalArgumentException- ifresponsesis empty
-
onPages
public MockRestServer onPages(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams, MockResponse... responses) Same asonPages(HTTPMethod, String, MockResponse...), but only matches a request whose query params contain every entry inrequiredQueryParams- for a path also serving other, non-paginated traffic that a bareonPages(HTTPMethod, String, MockResponse...)route would otherwise intercept too.- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholdersrequiredQueryParams- the query params that must be present with these exact values for this route to matchresponses- the page sequence, in order; must not be empty- Returns:
- this server
- Throws:
IllegalArgumentException- ifresponsesis empty
-
remove
Removes a route registered viaon(HTTPMethod, String, MockResponse), so a later request to that path falls through to any other registered route, theenqueue(com.shri.restinpeace.mock.MockResponse)queue, or the default "no response was queued or registered" failure - without a fullreset(), which would also wipe every other route, the queue, and recorded-request history. A no-op (returningfalse) if no such route is registered.- Parameters:
httpMethod- the HTTP method of the route to removepathTemplate- the path template of the route, exactly as passed toon(HTTPMethod, String, MockResponse)- Returns:
trueif a route was removed,falseif none matched
-
remove
public boolean remove(HTTPMethod httpMethod, String pathTemplate, Map<String, String> requiredQueryParams) Same asremove(HTTPMethod, String), for a route registered viaon(HTTPMethod, String, Map, MockResponse)with the samerequiredQueryParams.- Parameters:
httpMethod- the HTTP method of the route to removepathTemplate- the path template of the route, exactly as passed toon(HTTPMethod, String, Map, MockResponse)requiredQueryParams- the required query params of the route, exactly as passed toon(HTTPMethod, String, Map, MockResponse)- Returns:
trueif a route was removed,falseif none matched
-
reset
Clears every queued response, registered route, and recorded request, so the same server can be reused across multiple tests instead of paying to start a new one for each - e.g. from a@BeforeEachmethod, with the server itself started once in a@BeforeAll.- Returns:
- this server
-
takeRequest
Removes and returns the oldest recorded request, for asserting against one call at a time in the order calls were made.- Returns:
- the oldest recorded request
- Throws:
NoSuchElementException- if no request has been recorded
-
getRecordedRequests
Returns every request recorded so far, oldest first, without consuming them - unliketakeRequest().- Returns:
- the recorded requests
-
requestCount
public int requestCount()Returns how many requests have been recorded so far.- Returns:
- the recorded request count
-
countOf
Returns how many recorded requests matchhttpMethodandpathTemplate- a check for "was this endpoint called, and how many times" without manually filteringgetRecordedRequests()or looping overtakeRequest().- Parameters:
httpMethod- the HTTP method to matchpathTemplate- the path to match, with optional{name}placeholders, using the same syntax ason(HTTPMethod, String, MockResponse)- Returns:
- how many recorded requests matched
-
getUnhitRoutes
Returns every registered route that hasn't matched any recorded request yet, as"METHOD path"strings (e.g."GET /orders/{id}") - a coverage check for a route left registered after the code path that used to exercise it was removed, which otherwise causes no failure at all.- Returns:
- the unhit routes, in registration order, or an empty list if every route has been hit at least once
-
close
public void close()Stops the server.- Specified by:
closein interfaceAutoCloseable
-