2012-04-13 34 views
5

我有一个类:如何在Android中使用内置框架对使用HttpClient的类进行单元测试?

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) {...} 

} 

我不能找到一种方法,使用一个标准的框架来测试它。特别是,我需要模拟测试中丢失的全部互联网。

在执行测试时,有人建议手动关闭仿真器中的互联网。但在我看来,这不是一个好的解决方案,因为自动测试应该是......自动的。

我在类中添加了一个“client”字段,试图从测试类中嘲笑它。但是HttpClient接口的实现看起来相当复杂。

Robolectric框架允许开发者测试Http connection据我所知。但我想有一些方法可以在不使用这么大的额外框架的情况下编写这样的测试。

那么是否有任何使用HttpClient的单元测试类的简短方法?你在项目中如何解决这个问题?

回答

6

我在该类中添加了一个“client”字段,试图从测试类中嘲笑它。但是HttpClient接口的实现看起来相当复杂。

我对这个说法有点迷惑。在问题标题中,您询问关于单元测试的httpClint,通过模拟FakeHttpClient可以帮助您对除httpClient之外的其他应用程序部分进行单元测试,但对单元测试httpClient没有任何帮助。你需要的是一个用于单元测试httpClient的FakeHttpLayer(没有远程服务器,网络需要,因此单元测试)。

HttpClient的假人的测试:

如果你只需要检查在互联网丢失情况的应用程序的行为,那么一个经典的Android仪器测试就足够了,你可以通过编程方式接入互联网的仿真器关闭,而执行测试:

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

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

HttpClient的高级测试:

如果你想更精确地测试应用程序的行为,比如,你想测试你一个HTTP调用的应用程序,看它是否是正确处理不同的HTTP响应。 Robolectric是最好的选择。您可以使用FakeHttpLayer并嘲讽http请求和响应任何你喜欢的。

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

使用Robolectric的一些优点是:

  • 纯粹的JUnit测试,没有仪器测试所以不需要启动模拟器(或真实设备)运行测试,提高了开发速度。
  • 最新的Robolectric支持启用/禁用FakeHttpLayer的一行代码,您可以在其中设置http请求由FakeHttpLayer进行解释(不通过网络实现http呼叫),也可以设置http请求绕过FakeHttpLayer(执行真正的http呼叫网络)。查看this SO question了解更多详情。

如果您查看Robolectric的来源,您可以看到自己正确实施FakeHtppLayer非常复杂。我建议使用现有的测试框架,而不是实现自己的API。

希望这会有所帮助。

+0

谢谢你的回答。那么,标题说单元测试使用HttpClient的一些类,我不是说测试非常HttpClient。对不起,如果我没有说清楚。 WiFi管理器的选项非常棒,但手机通过移动网络连接。我试过了飞机模式,它仍然连接。所以,我猜,Robolectric是唯一的选择。谢谢。 – 2012-04-16 02:51:52

相关问题