本文介绍了如何进行单元测试使用内置的框架,使用HttpClient的Andr​​oid中的一类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类:

 公共类WebReader实现IWebReader {

    HttpClient的客户端;

    公共WebReader(){
        客户端=新DefaultHttpClient();
    }

    公共WebReader(HttpClient的HttpClient的){
        客户端= HttpClient的;
    }

    / **
     *在读取与给出的PARAMS指定的路径是网络资源。
     *资源@参数路径的路径被读取。
     * @参数需要PARAMS参数被转移到使用POST方法的服务器。
     * @参数COM pression如果它需要使用COM pression。默认为< B>真< / B&取代。
     * @返回< P>返回从服务器字符串了。如果出现了错误的下载文件,
     *将返回一个空字符串,有关错误的信息写入日志文件< / P>
     * /
    公共字符串readWebResource(字符串路径,ArrayList的< BasicNameValuePair>参数,可以布尔COM pression){
            HttpPost httpPost =新HttpPost(路径);
            字符串结果=;

            如果(COM pression)
                httpPost.addHeader(接受编码,GZIP);
            如果(params.size()大于0){
                尝试 {
                    httpPost.setEntity(新UrlEn codedFormEntity(PARAMS,UTF-8));
                }赶上(UnsupportedEncodingException E1){
                    e1.printStackTrace();
                }
            }

            尝试 {
                HTT presponse响应= client.execute(httpPost);
                状态行状态行= response.getStatusLine();
                INT状态code = statusLine.getStatus code();
                如果(状态code == 200){
                    HttpEntity实体= response.getEntity();
                    InputStream的内容= entity.getContent();
                    如果(entity.getContentEncoding()!=空
                            &功放;&安培; gzip的.equalsIgnoreCase(entity.getContentEncoding()
                                    .getValue()))
                        结果= uncom pressInputStream(内容);
                    其他
                        结果= convertStreamToString(内容);
                } 其他 {
                    Log.e(MyApp.class.toString(),无法下载文件);
                }
            }赶上(ClientProtocolException E){
                e.printStackTrace();
            }赶上(IOException异常E){
                e.printStackTrace();
            }

            返回结果;
        }

    私人字符串uncom pressInputStream(InputStream中的InputStream)
            抛出IOException异常{...}

    私人字符串convertStreamToString(InputStream的是){...}

}
 

我不能找到一种方法,使用一个标准的框架来测试它。特别是,我需要模拟从测试里面总上网丢失。

有建议手动打开互联网在模拟器中关闭,同时进行测试。但是,在我看来是不太好的解决办法,因为自动测试应该是...是自动的。

我添加了一个客户字段添加到该类试图从测试类内部嘲笑它。但实施HttpClient的界面看起来相当复杂。

借助 Robolectric 框架允许开发人员测试 据我所知HTTP连接。但我想有一些方法不使用这么大的额外的框架写这样的测试。

那么,有没有使用HttpClient的单元测试类的所有短期和直接的方式?你是如何在你的项目中解决这个问题?

解决方案

我感到有点混淆对本声明。从问题的称号,你问的单元测试httpClint,用嘲讽一个FakeHttpClient可以帮助您单元测试等应用程序的一部分,除了HttpClient的,但并没有什么帮助的单元测试的HttpClient。你需要的是一个FakeHttpLayer进行单元测试的HttpClient(没有远程服务器,网络需要,因此单元测试)。

HttpClient的假人的测试:

如果你只需要检查在网络丢失情况的应用程序的行为,那么一个经典的Andr​​oid仪器测试就足够了,可以通过编程把互联网在模拟器中关闭,同时进行测试:

 公共无效testWhenInternetOK(){
  ......
  webReader.readWebResource();
  //期望HTTP 200响应。
  ......
}

公共无效testWhenInternetLost(){
  ......
  wifiManager =(WifiManager)this.getSystemService(Context.WIFI_SERVICE);
  wifiManager.setWifiEnabled(假);
  webReader.readWebResource();
  //期望没有HTTP响应。
......
}
 

这需要远程HTTP服务器是完全安装并处于工作状态,而当您运行测试类,一个真正的HTTP通信是由通过网络,打HTTP服务器上。

HttpClient的高级测试:

如果你想pcisely测试应用程序的行为更多的$ P $,例如,你想在你的应用程序,看它是否是正确处理不同的HTTP响应测试HTTP调用

。该Robolectric是最好的选择。您可以使用FakeHttpLayer和模拟HTTP请求和响应,以任何你喜欢的。

 公共无效设置(){
  字符串URL =HTTP:// ...;
  发射测试//第一个HTTP请求,模拟一个HTTP 200响应(ContentType的:应用程序/ JSON)
  。HTT presponse response1 =新DefaultHtt presponseFactory()newHtt presponse(HttpVersion.HTTP_1_1,200,空);
  BasicHttpEntity使用实体=新BasicHttpEntity();
  entity1.setContentType(应用/ JSON);
  response1.setEntity(使用实体);
  发射测试//二http请求,模拟一个HTTP 404响应(ContentType的:text / html的)
  。HTT presponse响应2 =新DefaultHtt presponseFactory()newHtt presponse(HttpVersion.HTTP_1_1,404,空);
  BasicHttpEntity实体2 =新BasicHttpEntity();
  entity2.setContentType(text / html的);
   response2.setEntity(实体2);
  名单< Htt的presponse>响应=新的ArrayList< Htt的presponse>();
  responses.add(response1);
  responses.add(响应2);
  Robolectric.addHtt presponseRule(新FakeHttpLayer.UriRequestMatcher(POST,网址),响应);
}

公共无效命名为testFoo(){
  ......
  webReader.readWebResource(); //<  - 一个调用执行HTTP POST请求的URL。
  //期望HTTP 200响应。
  ......
}

公共无效testBar(){
  ......
  webReader.readWebResource(); //<  - 一个调用执行HTTP POST请求的URL。
  //期望HTTP 404响应。
......
}
 

使用Robolectric的一些优点是:

  • 在纯粹的JUnit测试,没有仪器测试所以不需要启动仿真器(或实际设备)运行测试,提高了开发速度。
  • 的code最新Robolectric支持单行启用/禁用FakeHttpLayer,在这里你可以设置HTTP请求中除$ P $由FakeHttpLayer PTED(通过网络没有真正的HTTP调用),或设置HTTP请求绕行FakeHttpLayer(执行通过网络真实的HTTP调用)。看看this SO质疑了解更多详情。

如果你看看Robolectric的来源,你可以看到它是很复杂的,以自己正确实施FakeHtppLayer。我会建议使用实现自己的API的现有测试框架来代替。

希望这有助于。

I've got a class:

public class WebReader implements IWebReader {

    HttpClient client;

    public WebReader() {
        client = new DefaultHttpClient();
    }

    public WebReader(HttpClient httpClient) {
        client = httpClient;
    }

    /**
     * Reads the web resource at the specified path with the params given.
     * @param path Path of the resource to be read.
     * @param params Parameters needed to be transferred to the server using POST method.
     * @param compression If it's needed to use compression. Default is <b>true</b>.
     * @return <p>Returns the string got from the server. If there was an error downloading file, 
     * an empty string is returned, the information about the error is written to the log file.</p>
     */
    public String readWebResource(String path, ArrayList<BasicNameValuePair> params, Boolean compression) {
            HttpPost httpPost = new HttpPost(path);
            String result = "";

            if (compression)
                httpPost.addHeader("Accept-Encoding", "gzip");
            if (params.size() > 0){
                try {
                    httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
                } catch (UnsupportedEncodingException e1) {
                    e1.printStackTrace();
                }
            }

            try {
                HttpResponse response = client.execute(httpPost);
                StatusLine statusLine = response.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                if (statusCode == 200) {
                    HttpEntity entity = response.getEntity();
                    InputStream content = entity.getContent();
                    if (entity.getContentEncoding() != null
                            && "gzip".equalsIgnoreCase(entity.getContentEncoding()
                                    .getValue()))
                        result = uncompressInputStream(content);
                    else
                        result = convertStreamToString(content);
                } else {
                    Log.e(MyApp.class.toString(), "Failed to download file");
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return result;
        }

    private String uncompressInputStream(InputStream inputStream)
            throws IOException {...}

    private String convertStreamToString(InputStream is) {...}

}

I cannot find a way to test it using a standard framework. Especially, I need to simulate total internet lost from inside the test.

There are suggestions to manually turn the Internet in the emulator off while performing the test. But it seems to me as not quite a good solution, because the automatic tests should be... automatic.

I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.

The Robolectric framework allows the developers to test Http connection as far as I know. But I guess there is some way to write such a test without using so big additional framework.

So are there any short and straightforward ways of unit testing classes that use HttpClient? How did you solve this in your projects?

解决方案

I am a little bit confuse about this statement. From the question title, you are asking about unit-testing httpClint, by mocking a FakeHttpClient may help you unit-testing other part of app except httpClient, but doesn't help anything for unit-testing httpClient. What you need is a FakeHttpLayer for unit-testing httpClient (no remote server, network requires, hence unit-testing).

HttpClient Dummy Test:

If you only need examine app behavior in the situation that internet is lost, then a classic Android Instrument Test is sufficient, you can programmatically turn the Internet in the emulator off while performing the test:

public void testWhenInternetOK() {
  ... ...
  webReader.readWebResource();
  // expect HTTP 200 response.
  ... ...
}

public void testWhenInternetLost() {
  ... ...
  wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE); 
  wifiManager.setWifiEnabled(false);
  webReader.readWebResource();
  // expect no HTTP response.
... ...
}

This requires the remote http server is completely setup and in a working state, and whenever you run your test class, a real http communication is made over network and hit on http server.

HttpClient Advanced Test:

If you want to test app behavior more precisely, for instance, you want to test a http call in you app to see if it is handle different http response properly. the Robolectric is the best choice. You can use FakeHttpLayer and mock the http request and response to whatever you like.

public void setup() {
  String url = "http://...";
  // First http request fired in test, mock a HTTP 200 response (ContentType: application/json)
  HttpResponse response1 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 200, null);
  BasicHttpEntity entity1 = new BasicHttpEntity();
  entity1.setContentType("application/json");
  response1.setEntity(entity1);
  // Second http request fired in test, mock a HTTP 404 response (ContentType: text/html)
  HttpResponse response2 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 404, null);
  BasicHttpEntity entity2 = new BasicHttpEntity();
  entity2.setContentType("text/html");
   response2.setEntity(entity2);
  List<HttpResponse> responses = new ArrayList<HttpResponse>();
  responses.add(response1);
  responses.add(response2);
  Robolectric.addHttpResponseRule(new FakeHttpLayer.UriRequestMatcher("POST", url), responses);
}

public void testFoo() {
  ... ...
  webReader.readWebResource(); // <- a call that perform a http post request to url.
  // expect HTTP 200 response.
  ... ...
}

public void testBar() {
  ... ...
  webReader.readWebResource(); // <- a call that perform a http post request to url.
  // expect HTTP 404 response.
... ...
}

Some pros of using Robolectric are:

  • Purely JUnit test, no instrument test so don't need start emulator (or real device) to run the test, increase development speed.
  • Latest Robolectric support single line of code to enable/disable FakeHttpLayer, where you can set http request to be interpreted by FakeHttpLayer (no real http call over network), or set the http request bypass the FakeHttpLayer(perform real http call over network). Check out this SO question for more details.

If you check out the source of Robolectric, you can see it is quite complex to implement a FakeHtppLayer properly by yourself. I would recommend to use the existing test framework instead of implementing your own API.

Hope this helps.

这篇关于如何进行单元测试使用内置的框架,使用HttpClient的Andr​​oid中的一类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-22 20:05