简体   繁体   English

尝试使用 Mockito 模拟 Apache HTTP 客户端时出现的问题

[英]Issues when trying to mock Apache HTTP Client with Mockito

I am a beginner ramping up on Mockito.我是 Mockito 的初学者。 I have an HTTP get call which I would like to mock.我有一个我想模拟的 HTTP get 调用。

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这是我的 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;
    }
}

As, mclient isnt initiated, I keep getting this error由于未启动 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: 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 UPDATE1:我已经合并了@glytching 的评论并删除了 setup() 中的变量初始化我已经合并了 @Maciej Kowalski 的评论并使客户端成为 Utility 类中的类变量

UPDATE 2:更新 2:

The NPE error has been resolved after resolving to the right import -> import org.mockito.Mock; NPE 错误在解析为正确的 import -> import org.mockito.Mock 后已解决;

But, the issue of not seeing mocked response still exists但是,没有看到模拟响应的问题仍然存在

You declare mClient as a mock here:您在此处将mClient声明为模拟:

@Mock
private HttpClient mClient;

But you also assign it a concrete instance of DefaultHttpClient in setUp() :但是你也在setUp()为它分配了一个DefaultHttpClient的具体实例:

mClient = new DefaultHttpClient();

Just remove that line from setup() and the instance of mClient in use in your test will be a mock.只需从setup()删除该行,测试中使用的mClient实例将是一个模拟。

Update : your updated question shows this import:更新:您更新的问题显示此导入:

import org.easymock.Mock;

That should be那应该是

import org.mockito.Mock;

This instruction: MockitoAnnotations.initMocks(this);这条指令: 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.将初始化任何用org.mockito.Mock注释的类成员,它不会对用org.easymock.Mock注释的类成员做任何事情,所以我怀疑mClient没有被初始化,因此是 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 ...注意:在GoogleAccessor内部也有一个问题,你在这里在getGoogle显式实例化了HttpClient ......

HttpClient client = new DefaultHttpClient();

... so this method will never use the mocked HttpClient . ...所以这个方法永远不会使用模拟的HttpClient

If you comment out this line:如果你注释掉这一行:

HttpClient client = new DefaultHttpClient();

... and add this class member to GoogleAccessor : ...并将此类成员添加到GoogleAccessor

private HttpClient client;

... then you'll find that your mocked client will be used inside getGoogle() . ...然后你会发现你的getGoogle()客户端将在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 .在您的测试用例中,您使用的是@InjectMocks因此您指示测试运行程序将您的模拟注入GoogleAccessor但由于没有该类型的类成员,因此测试运行程序无需将模拟分配. 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 .此外,即使它可以模拟分配到的东西,你在里面声明HTTP客户端getGoogle()从而迫使该方法的所有调用使用的具体实例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:然后,您需要监视被测类以保持getNewClient方法的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();
}

you also need to use the doReturn().when() syntax instead of when().then() as you are dealing with a spy.在处理间谍doReturn().when()您还需要使用doReturn().when()语法而不是when().then()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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