尝试使用Mocktio模拟Apache HTTP Client时出现的问题

时间:2018-01-29 11:14:02

标签: java unit-testing junit mockito

我是Mockito的初学者。我有一个HTTP get get call,我想嘲笑。

必须进行测试的Utility文件就是这个。

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);
        }
    }

}

这是我的jUnit测试

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;
    }
}

因为,mclient没有启动,我不断收到此错误

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:我收录了来自@glytching&的评论。删除了setup()中的变量初始化 我收录了来自@Maciej Kowalski的评论,并使客户端成为Utility class

中的一个类变量

更新2:

解析到正确的导入后,NPE错误已解决 - > import org.mockito.Mock;

但是,没有看到模拟反应的问题仍然存在

2 个答案:

答案 0 :(得分:1)

您正在方法本身中创建客户端。除非您创建包级别方法,否则无法模拟它:

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

并在方法中使用它。

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

然后你需要监视测试中的类,以保持测试方法不变,并模拟getNewClient方法返回你想要的东西:

@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();
}

在处理间谍时,您还需要使用doReturn().when()语法而不是when().then()

答案 1 :(得分:1)

您在此处声明mClient为模拟:

@Mock
private HttpClient mClient;

但是你也在DefaultHttpClient中为setUp()分配了一个具体的实例:

mClient = new DefaultHttpClient();

只需从setup()中移除该行,您测试中使用的mClient实例就是模拟。

更新:您更新的问题显示了此导入信息:

import org.easymock.Mock;

那应该是

import org.mockito.Mock;

该指令:MockitoAnnotations.initMocks(this);将初始化任何使用org.mockito.Mock注释的类成员,它不会对使用org.easymock.Mock注释的类成员执行任何操作,因此我怀疑mClient没有初始化,因此NPE。当我在本地运行您的代码时,如果导入正确,则测试成功完成,没有例外。

注意:GoogleAccessor内部也存在问题,您HttpClient明确地在getGoogle内实例{/ p>

HttpClient client = new DefaultHttpClient();

...所以此方法永远不会使用模拟的HttpClient

如果你注释掉这一行:

HttpClient client = new DefaultHttpClient();

...并将此类成员添加到GoogleAccessor

private HttpClient client;

...然后您会发现您的模拟客户端将在getGoogle()内使用。

背景;为了在类中使用模拟实例,您必须为测试运行器提供一些方法来注入该模拟实例到类中。在您的测试用例中,您正在使用@InjectMocks,因此您指示测试运行器将模拟注入GoogleAccessor但由于没有该类型的类成员,测试运行器没有任何内容可以分配模拟的。此外,即使它可以将模拟分配给某些东西,您也在声明http客户端内部 getGoogle(),从而强制该方法的所有调用都使用DefaultHttpClient的具体实例