0

i have the following setup:

public String loadFile(String uri) throws ClientProtocolException, IOException {
        StringBuilder resp = new StringBuilder();
        HttpClient client = HttpClientBuilder.create().build();
        HttpGet request = new HttpGet(uri);
        HttpResponse response = client.execute(request);
}

with the Testclass looking like this:

@RunWith(JMockit.class)
public class FakeLoaderTest {

    FakeLoader loader = new FakeLoader(); //class under test

    @Test
    public void testLoadFile(   
        @Mocked @Cascading final HttpClientBuilder mockBuilder,
        @Capturing  final HttpClient mockClient
    ) throws IOException, URISyntaxException{

        new Expectations() {{
            HttpClientBuilder.create().build(); result =  mockClient;
            mockClient.execute(withAny(mockget)); result =  new IOException("test - test");
        }};

        loader.loadFile();
    }
}

this gives me "Unexpected Invocation of ClosableHttpClient.execute - expected was HttpClient.execute"

HttpClientBuilder.create.build() returns a ClosableHttpClient, which implements HttpClient. I thought @Capturing took care of Mocking all classes extending the Class/interface in question?

This works fine, as expected:

    @Mocked @Cascading final HttpClientBuilder mockBuilder,
    @Capturing  final **Closeable**HttpClient mockClient

But I want to test against the Interface, because I am not testing the HttpClient implementation that is used. In this test i dont care if someone at apache decides that ClosableHttpClient is succeeded by "ThinHttpCLient" as default implementation, as long as apache sticks to the HttpClient interface i do not want to alter this testcase. It is about testing the internal handling of an IO Exception (eg. does he Log to the right location? does he retry correctly etc.)

How do i handle this using JMockit and JUnit?

thanks in advance,

BillDoor

billdoor
  • 1,999
  • 4
  • 28
  • 54

1 Answers1

1

The test should be as follows:

@Test(expected = IOException.class)
public void loadFile(
    @Cascading HttpClientBuilder mockBuilder, 
    @Mocked final CloseableHttpClient mockClient
) throws Exception
{
    new NonStrictExpectations() {{
        mockClient.execute((HttpGet) any); result = new IOException("test");
    }};

    loader.loadFile("uri");
}

The return type of HttpClientBuilder#build() is CloseableHttpClient, which is an abstract class containing common method implementations. The Apache project can never change this return type to something else, because it would be a breaking API change, causing compilation errors.

Also, @Capturing does nothing more that @Mocked in this case, since the use of @Cascading HttpClientBuilder implies that no actual implementation class will be instantiated.

Rogério
  • 16,171
  • 2
  • 50
  • 63
  • @Cascading (i i got it right) means that objects returned by a chained-call will all be mocked too, so i put it for build().create()'s sake, but thanks pointing out that it is not necessary. As for CloseableHttpClient beeing unchangable API, i remember using things like "new DefaultHttpClient()" which is now deprecated and has changed to HttpClientBuilder().build().create() beeing used. If apache provides an interface, i'd like to use it if possible. Are you saying you are >sure< it is not possible? - if so please just confirm and i'll accept the answer – billdoor Sep 17 '14 at 06:41
  • @billdoor `DefaultHttpClient` extends `CloseableHttpClient`, which is effectively the true base type for the library. I don't know exactly why having a Java interface was needed, but I would say that probably `HttpClient` should have been the abstract class which `CloseableHttpClient` is. So, yeah, I would be very surprised if they ever make the `build()` method return anything else. – Rogério Sep 17 '14 at 15:58