[
https://issues.apache.org/jira/browse/HTTPCLIENT-2216?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17539949#comment-17539949
]
Oleg Kalnichevski commented on HTTPCLIENT-2216:
-----------------------------------------------
[~dkrompet] I do not understand the issue. You should be mocking / injecting
`HttpAsyncClient` not `CloseableHttpAsyncClient`.
Client services should be using `HttpAsyncClient` not
`CloseableHttpAsyncClient`.
Oleg
> ClosableHttpAsyncClient interface and testing
> ---------------------------------------------
>
> Key: HTTPCLIENT-2216
> URL: https://issues.apache.org/jira/browse/HTTPCLIENT-2216
> Project: HttpComponents HttpClient
> Issue Type: Improvement
> Components: HttpClient (async)
> Affects Versions: 5.0
> Reporter: Dillon Krompetz
> Priority: Minor
>
> TLDR: Please insert interface that contains functionality of
> CloseableHttpAsyncClient so that this can be tested more easily.
> ===========
> The current CloseableHttpAsyncClient implementation currently has a problem
> in its interface and intended usage such that testing becomes more difficult
> than it needs to be.
>
> Class in question
> {code:java}
> public abstract class CloseableHttpAsyncClient implements HttpAsyncClient,
> ModalCloseable {code}
>
> Methods in question
> {code:java}
> public abstract void start(); {code}
> defined in CloseableHttpAsyncClient class
>
> {code:java}
> <T> Future<T> execute(
> AsyncRequestProducer requestProducer,
> AsyncResponseConsumer<T> responseConsumer,
> HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
> HttpContext context,
> FutureCallback<T> callback
> ); {code}
> defined in HttpAsyncClient interface
>
> {code:java}
> void close(CloseMode closeMode); {code}
> defined in ModalCloseable interface
>
> How is this an issue?
> When a developer goes to make use of the Http5 async client they may follow
> the guides provided:
> [https://hc.apache.org/httpcomponents-client-5.1.x/examples-async.html] and
> see that they need access to the above three methods.
>
> They may introduce an adapter around this interface to take thier business
> logic and convert it into an Http request compatable with the client.
>
> {code:java}
> class MyHttp5AsyncClient {
> private CloseableHttpAsyncClient client;
>
> public MyHttp5AsyncClient(CloseableHttpAsyncClient httpAsyncClient) {
> this.client = httpAsyncClient;
> }
>
> public start() {
> this.client.start();
> }
>
> public stop() {
> this.client.close();
> }
>
> public execute(MyRequest request) {
> AsyncRequestProducer requestProducer = toRequestProducer(request);
> Future<SimpelHttpResponse> result = client.execute(
> requestProdcuer,
> SimpleResponseConsumer.create(),
> null,
> null,
> null
> );
> return result.get();
> }
> } {code}
>
> Note that in the CloseableHttpAsyncClient implementation that the execute
> method is final:
> {code:java}
> public final <T> Future<T> execute(
> final HttpHost target,
> final AsyncRequestProducer requestProducer,
> final AsyncResponseConsumer<T> responseConsumer,
> final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
> final HttpContext context,
> final FutureCallback<T> callback) {
> Args.notNull(requestProducer, "Request producer");
> Args.notNull(responseConsumer, "Response consumer");
> return doExecute(target, requestProducer, responseConsumer,
> pushHandlerFactory, context, callback);
> } {code}
>
> Testing
> Popular testing patterns include injecting the dependency into the class to
> verify that methods were called with certain arguments or to mock the result
> of calling a method to control the code flow in isolation. A very popular
> Java mocking and verifying library might be Mockito where a developer may
> write something like:
> {code:java}
> class MyHttp5AsyncClientTest {
>
> @Mock
> private CloseableHttpAsyncClient client;
>
> private MyHttp5AsyncClient toTest;
>
> @BeforeEach
> public void setup() {
> toTest = new MyHttp5AsyncClient(client);
> }
>
> @Test
> public void execute_whenCalledWithSomeInput_thenWillDoTheThing() {
> when(client.execute(
> any(AsyncRequestProducer.class),
> any(AsyncResponseConsumer.class),
> nullable(HandlerFactory.class),
> nullable(HttpContext.class),
> nullable(FutureCallback.class)
> )).thenReturn(
> CompletableFuture.completedFuture(null)
> );
>
> var result = toTest.execute(null);
> // ...
> // some expectations on the result that are interesting.
> }
> } {code}
> This test will fail because the hard implementation of
> CloseableHttpAsyncClient execute is final and when mocking it will call the
> actual method with the result of calls to Mockito's any() which returns
> nulls. This will then be caught by the guard statements in the actual method
> call to execute.
>
> How can this be made better?
> How I got around the issue:
> Inserted a wrapper around CloseableHttpAsyncClient that had the public
> methods I needed to use that could be mocked.
>
> How might this be better resolved:
> 1. Remove final modifier from the CloseableHttpAsyncClient methods that are
> likely to be mocked.
> There is likely a reason these methods were intended to not be open for
> modification :)
>
> 2. Insert an interface that CloseableHttpAsyncClient implements that captures
> its intended functionality.
> This new interface would include the public methods of
> CloseableHttpAsyncClient and extend the interfaces for HttpAsyncClient and
> ModalCloseable
--
This message was sent by Atlassian Jira
(v8.20.7#820007)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]