
Product
Reachability for Ruby Now in Beta
Reachability analysis for Ruby is now in beta, helping teams identify which vulnerabilities are truly exploitable in their applications.
io.github.sola-ris:jax-rs-client-test
Advanced tools
A library for testing classes that rely on a JAX-RS Client without starting a full server or relying on mocking libraries.
A library for testing classes that use a JAX-RS Client without starting a full server or relying on mocking libraries.
Heavily inspired by Spring's Client testing infrastructure.
<dependency>
<groupId>io.github.sola-ris</groupId>
<artifactId>jax-rs-client-test</artifactId>
<version>0.1.0</version>
</dependency>
The idea is to declare the expected requests and provide "mock" or "stub" responses so the code can be tested in isolation, i.e. without starting a server.
The following example shows how to do so with a Client:
Client client = ClientBuilder.newClient();
MockRestServer server = MockRestServer.bindTo(client).build();
server.expect(RequestMatchers.requestTo("/users/42"))
.andRespond(MockResponseCreators.withNotFound());
// Test code that uses the Client
server.verify();
In the example, the MockRestServer (the entrypoint into the library) configures the Client with a ClientRequestFilter that matches the incoming
request against the set-up expectations and returns the "stub" responses. In this case, we expect a call to /users/42 and want to return
a response with status code 404. Additional expected requests and stub responses can be defined as needed. At the end of the test, server.verify()
can be used to verify that all the expected requests were indeed executed.
Unless specified otherwise, each expected request is expected to be executed exactly once. The expect method of the MockRestServer provides an
overload that accepts an ExpectedCount argument that specifies a range count (e.g. once, between, min, max, etc.) for the given request expectation.
The following example shows how to do so with a WebTarget:
WebTarget target = ClientBuilder.newClient().target("");
MockRestServer server = MockRestServer.bindTo(target).build();
server.expect(ExpectedCount.max(3), RequestMatchers.requestTo("/hello"))
.andRespond(MockResponseCreators.withSuccess());
server.expect(ExpectedCount.between(1, 2), RequestMatchers.requestTo("/goodbye"))
.andRespond(MockResponseCreators.withSuccess());
// Use the bound WebTarget e.g. by passing it to the class under test
server.verify();
By default, only the first invocation of each expected request is expected to occur in order of declaration.
This behavior can be changed by passing the desired RequestOrder to withRequestOrder when building the server.
ClientBuilder clientBuilder = ClientBuilder.newBuilder()
MockRestServer server = MockRestServer.bindTo(clientBuilder)
.withRequestOrder(RequestOrder.UNORDERED)
.build();
The following options are available:
In some tests it may be necessary to mock only some of the requests and call an actual remote service or others.
This can be done through an ExecutingResponseCreator:
Client client = ClientBuilder.newClient();
MockRestServer server = MockRestServer.bindTo(client).build();
server.expect(RequestMatchers.requestTo("/profile/42"))
.andRespond(new ExecutingResponseCreator()); // <1>
Client customClient = ClientBuilder.newBuilder().register(new MyAuthFilter()).build(); // <2>
server.expect(RequestMatchers.requestTo("/users/42")) // <3>
.andExpect(RequestMatchers.method("GET"))
.andRespond(new ExecutingResponseCreator(customClient));
server.expect(RequestMatchers.requestTo("/users/42")) // <4>
.andExpect(RequestMatchers.method("DELETE"))
.andRespond(MockResponseCreators.withSuccess());
// Test code that uses the Client
customClient.close(); // <5>
server.verify();
/profile/42 by calling the actual service with a default ClientGET /users/42 by calling the actual service through customClientDELETE /users/42 with a stubExecutingResponseCreator must be closed by the callerJAX-RS Client Test comes with a number of built-in RequestMacher implementations, all accessed via factory methods in RequestMatchers.
Custom request matchers can be implemented as a lambda and can access the current ClientRequestContext.
All implementations must throw an AssertionError if the incoming request does not match and return if it does.
For example:
RequestMatcher myMatcher = request -> {
if (Locale.ENGLISH.equals(request.getLanguage())) {
throw new AssertionError("Expected a language other than ENGLISH.")
}
};
When implementing a RequestMatcher, the entity can be accessed via ClientRequestContext#getEntity.
If the matcher requires a different representation of the entity, e.g. a JSON String instead of POJO, it can be converted via an EntityConverter,
which can be obtained by calling EntityConverter#fromRequestContext. The EntityConverter has the same capabilities as the client that made the
request, so if the client has a provider that can handle POJO -> JSON String conversion, the EntityConverter can do it as well.
For example:
RequestMatcher myMatcher = request -> {
EntityConverter converter = EntityConverter.fromRequestContext(request);
// Assuming the MediaType is application/json
String jsonString = converter.convertEntity(request, String.class);
// Assertions on the string
};
multipart/form-data (EntityPart)EntityPart has certain limitations that require it to be handled separately from other request entities:
InputStream based, meaning its contents can only be accessed onceequals, preventing comparison with another EntityPartThe EntityConverter provides two methods to work around this:
bufferExpectedMultipart, which takes an arbitrary List<EntityPart> and buffers itbufferMultipartRequest, which takes the List<EntityPart> from the current ClientRequestContext,
buffers it and sets the request up for further processing or repeated buffering in another RequestMatcherFor example:
EntityPart myEntityPart = EntityPart.withName("username")
.mediaType(MediaType.TEXT_PLAIN_TYPE)
.content("admin")
.build();
RequestMatcher myMultipartMatcher = request -> {
EntityConverter converter = EntityConverter.fromRequestContext(request);
EntityPart myBufferedPart = converter.bufferExpectedMultipart(List.of(myEntityPart)).get(0);
// MediaType multipart/form-data and entity of type List<EntityPart> is assumed
EntityPart bufferedRequestPart = converter.bufferMultipartRequest(request).get(0);
// Assuming AssertJ is present
// Reading the content here does not prevent further reads
// in another matcher or during request execution
assertThat(myBufferedPart.getContent(String.class))
.isEqualTo(bufferedRequestPart.getContent(String.class))
// Buffered EntityParts implement equals()
assertThat(myBufferedPart)
.isEqualTo(bufferedRequestPart)
};
// Set up the MockRestServer and execute the request
The library relies on a ClientRequestFilter that calls ClientRequestContext#abortWith in order to create its mock response, making it impossible to test classes that use a client that relies on the same behavior.
FAQs
Unknown package
We found that io.github.sola-ris:jax-rs-client-test demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Reachability analysis for Ruby is now in beta, helping teams identify which vulnerabilities are truly exploitable in their applications.

Research
/Security News
Malicious npm packages use Adspect cloaking and fake CAPTCHAs to fingerprint visitors and redirect victims to crypto-themed scam sites.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.