简体   繁体   中英

Issues when trying to mock Apache HTTP Client with Mockito

I am a beginner ramping up on Mockito. I have an HTTP get call which I would like to mock.

The Utility file which has to be tested is this.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class GoogleAccessor {
private HttpClient client ;

    public void getGoogle() throws ClientProtocolException, IOException {
        String url = "http://www.google.com/search?q=httpClient";
         client = new DefaultHttpClient();
        HttpGet request = new HttpGet(url);
        try {
            HttpResponse response = client.execute(request);
            BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer result = new StringBuffer();
            String line = "";
            while ((line = rd.readLine()) != null) {
                result.append(line);
            }
            System.out.println(result);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}

This is my jUnit test

import static org.mockito.Mockito.when;

import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.mockito.Mock;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

public class TestTest {

    @Mock
    private HttpClient mClient;

    @InjectMocks
    private GoogleAccessor dummy;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testAddCustomer_returnsNewCustomer() throws Exception {
        when(mClient.execute(Mockito.any())).thenReturn(buildResponse(201, "OK", "{\"message\":\"Model was stored\"}"));
        dummy.getGoogle();
    }

    BasicHttpResponse buildResponse(int statusCode, String reason, String body) {
        BasicHttpResponse response = new BasicHttpResponse(
                new BasicStatusLine(new ProtocolVersion("HTTP", 1, 0), statusCode, reason));
        if (body != null) {
            response.setEntity(new ByteArrayEntity(body.getBytes()));
        }

        return response;
    }
}

As, mclient isnt initiated, I keep getting this error

java.lang.NullPointerException
    at com.amazon.cartographertests.accessors.TestTest.testAddCustomer_returnsNewCustomer(TestTest.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    ...

UPDATE1: I have incorporated comments from @glytching & removed the variable initialization in setup() I have incorporated comments from @Maciej Kowalski and made the client a class variable in Utility class

UPDATE 2:

The NPE error has been resolved after resolving to the right import -> import org.mockito.Mock;

But, the issue of not seeing mocked response still exists

You declare mClient as a mock here:

@Mock
private HttpClient mClient;

But you also assign it a concrete instance of DefaultHttpClient in setUp() :

mClient = new DefaultHttpClient();

Just remove that line from setup() and the instance of mClient in use in your test will be a mock.

Update : your updated question shows this import:

import org.easymock.Mock;

That should be

import org.mockito.Mock;

This instruction: MockitoAnnotations.initMocks(this); wil initialise any class members annotated with org.mockito.Mock , it won't do anything to a class member annotated with org.easymock.Mock so I suspect that mClient is not being initialised and hence the NPE. When I run your code locally, with the correct import, the test completes successfully, no exceptions.

Note: there is also an issue inside GoogleAccessor , you instance the HttpClient explicitly inside getGoogle here ...

HttpClient client = new DefaultHttpClient();

... so this method will never use the mocked HttpClient .

If you comment out this line:

HttpClient client = new DefaultHttpClient();

... and add this class member to GoogleAccessor :

private HttpClient client;

... then you'll find that your mocked client will be used inside getGoogle() .

For background; in order to use a mocked instance inside a class you must provide some way for the test runner to inject that mocked instance into the class. In your test case you are using @InjectMocks so you are instructing the test runner to inject your mock into GoogleAccessor but since there is no class member of that type the test runner has nothing to assign the mock to . In addition, even if it could assign the mock to something, you were declaring the http client inside getGoogle() thereby forcing all invocations of that method to use a concrete instance of DefaultHttpClient .

You are creating the client in the method itself. There is no way you can mock that unless you create a package level method:

HttpClient getNewClient(){
   return new DefaultHttpClient();
}

and use it instead in the method.

public void getGoogle() throws ClientProtocolException, IOException {
    String url = "http://www.google.com/search?q=httpClient";
    HttpClient client = getNewClient();

Then you would need to spy the class under test in order to keep the method under test as it is and mock the getNewClient method to return what you want:

@Mock
private HttpClient mClient;

@InjectMocks
@Spy
private GoogleAccessor dummy;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

@Test
public void testAddCustomer_returnsNewCustomer() throws Exception {
    doReturn(null).when(mClient).execute(Mockito.any()));
    dummy.getGoogle();
}

you also need to use the doReturn().when() syntax instead of when().then() as you are dealing with a spy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM