Skip to main content

NG Mock REST Client

The juneau-rest-mock module provides two ways to test code that uses the NG RestClient without a network:

ClassUse it to test
org.apache.juneau.rest.mock.MockRestClientA @Rest resource, in-process. The request is dispatched straight to the resource's RestContext.
org.apache.juneau.rest.mock.MockHttpTransportClient code (a RestClient or @Remote proxy) against canned responses that you define.

See the guide: Test an endpoint with MockRestClient

The classic mock client is documented in Mock REST Client Overview.

MockRestClient (test a @Rest resource)​

MockRestClient wraps a RestClient whose transport calls the resource directly. The argument is a @Rest bean instance or class. Request paths are relative to the resource's @Rest(path), so for a PetResource annotated @Rest(path="/pets"), mock.get("/1") reaches /pets/1. A path of /pets/1 would look for /pets/pets/1 and return a 404.

try (MockRestClient mock = MockRestClient.create(PetResource.class);
var resp = mock.get("/1").run()) {
resp.assertThat().isOk().body().contains("Fido");
}

The verb methods (get, post, put, patch, delete, head, request) return the same RestRequest as a real client, so assertions, interceptors and typed bodies all work. getClient() exposes the underlying RestClient, for example to create a @Remote proxy:

PetApi api = mock.getClient().remote(PetApi.class);

Builder​

MockRestClient mock = MockRestClient.builder(new PetResource())
.contextPath("/app")
.defaultParser(Json5Parser.create().build())
.build();
MethodPurpose
contextPath(String)Simulates a servlet context path. The root URL becomes http://localhost + context path + servlet path.
overridingBeanStore(BeanStore)Supplies beans to the resource. Disables the per-class RestContext cache so your beans are used.
defaultParser(Parser)Parser for body().as(...).
noTrace()Sends No-Trace: true on every request; see below.

Without an overriding bean store, the RestContext is created once per resource class and reused across tests, so mock clients are cheap to create.

Keeping expected-error traces out of the log​

A test that asserts a 404, 405 or 415 normally makes the server log a stack trace for an outcome the test expects. Send No-Trace: true to keep the trace off the log record while the one-line access record (status, method, path) is still logged. Use noTrace() on the builder for every call from that client:

try (MockRestClient mock = MockRestClient.builder(PetResource.class).noTrace().build();
var resp = mock.get("/1/missing").run()) {
resp.assertStatus(404);
}

or on a single request, which leaves the other calls from the same client unchanged:

try (var resp = mock.get("/1/missing").noTrace().run()) {
resp.assertStatus(404);
}

No-Trace is opt-in only: ignoreErrors() and the other builder options do not turn it on. See Logging / Debugging for what the server does with the header.

Redirect and URL-policy behavior matches the real client; see NG Client Security.

MockHttpTransport (test client code)​

MockHttpTransport is an HttpTransport with routes you define. Plug it into a real RestClient:

var transport = MockHttpTransport.builder()
.on("GET", "/pets/1", req -> TransportResponse.builder()
.statusCode(200)
.header("Content-Type", "application/json")
.body(new ByteArrayInputStream("{\"name\":\"Fido\"}".getBytes(UTF_8)))
.build())
.on("POST", "/pets", req -> TransportResponse.builder().statusCode(201).build())
.recordRequests()
.build();

var client = RestClient.builder().transport(transport).rootUrl("http://mock").build();

try (var resp = client.get("/pets/1").run()) {
resp.assertOk();
}

assertEquals(1, transport.getRecordedRequests().size());
  • on(method, path, handler) matches the HTTP method and the exact path. A null method or path matches anything.
  • fallback(handler) handles unmatched requests. Without one, unmatched requests get a 404 with the body No mock route matched: METHOD path.
  • recordRequests() keeps every TransportRequest; read them with getRecordedRequests() and reset with clearRecordedRequests().
  • A handler can throw TransportException to simulate a network failure.
  • MockHttpTransport.of(status, body) is a one-liner that always returns the same response.

The transport reports supportsUrlPolicy() == true and follows redirects with the same re-validation loop as the production transports, so @Remote tests exercise the SSRF guard too.

MockPathResolver​

MockPathResolver resolves an incoming URL into the artifacts an HttpServletRequest would report: getTarget(), getContextPath(), getServletPath(), getRemainder(), getURI(), and getError(). Its constructor takes (target, contextPath, servletPath, pathToResolve, pathVars); {name} tokens in the context and servlet paths are filled from pathVars, and a null target defaults to http://localhost.

It is used by the classic org.apache.juneau.rest.mock.classic.MockRestClient. The NG MockRestClient does not use it; it derives its root URL from contextPath and the resource's servlet path. You only need MockPathResolver directly when writing your own classic-style mock harness.

The classic MockRestClient supplies its own connection manager, which the classic SSRF guard cannot pin, so its getRemote(...) proxies need allowPrivateUrls(true) on the builder (see NG Client Security).

See also​