[prev in list] [next in list] [prev in thread] [next in thread] 

List:       httpclient-commons-dev
Subject:    [jira] [Created] (HTTPCLIENT-2216) ClosableHttpAsyncClient interface and testing
From:       "Dillon Krompetz (Jira)" <jira () apache ! org>
Date:       2022-05-19 13:49:00
Message-ID: JIRA.13445843.1652968113000.124919.1652968140009 () Atlassian ! JIRA
[Download RAW message or body]

Dillon Krompetz created HTTPCLIENT-2216:
-------------------------------------------

             Summary: 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


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: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic