【问题标题】:Unit testing with Android volley使用 Android volley 进行单元测试
【发布时间】:2013-09-04 15:54:11
【问题描述】:

我想知道如何为 volley 框架创建单元测试。模拟请求和响应,以便我可以创建不需要 Web 服务工作和网络访问的单元测试。

我用谷歌搜索了它,但我根本找不到关于框架的太多信息

【问题讨论】:

  • 目前我能找到的文档为零。但是有大量的示例,Google IO 2013 视频也很容易挖掘。
  • 对于其他登陆这里的人,请考虑使用 OkHttp 而不是 Volley。这是一个更现代的框架,可以使用 OkHttp 的 MockWebServer 更轻松地进行测试。

标签: android unit-testing android-volley


【解决方案1】:

我实现了一个名为 FakeHttpStackHttpStack 子类,它从 res/raw 中的本地文件加载假响应正文。我这样做是为了开发目的,也就是说,我可以在服务器准备好之前为新 API 开发一些东西,但是你可以从这里学到一些东西(例如,覆盖 HttpStack#peformRequest 和 createEntity)。

/**
 * Fake {@link HttpStack} that returns the fake content using resource file in res/raw.
 */
class FakeHttpStack implements HttpStack {
    private static final String DEFAULT_STRING_RESPONSE = "Hello";
    private static final String DEFAULT_JSON_RESPONSE = " {\"a\":1,\"b\":2,\"c\":3}";
    private static final String URL_PREFIX = "http://example.com/";
    private static final String LOGGER_TAG = "STACK_OVER_FLOW";

    private static final int SIMULATED_DELAY_MS = 500;
    private final Context context;

    FakeHttpStack(Context context) {
        this.context = context;
    }

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> stringStringMap)
            throws IOException, AuthFailureError {
        try {
            Thread.sleep(SIMULATED_DELAY_MS);
        } catch (InterruptedException e) {
        }
        HttpResponse response
                = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, 200, "OK"));
        List<Header> headers = defaultHeaders();
        response.setHeaders(headers.toArray(new Header[0]));
        response.setLocale(Locale.JAPAN);
        response.setEntity(createEntity(request));
        return response;
    }

    private List<Header> defaultHeaders() {
        DateFormat dateFormat = new SimpleDateFormat("EEE, dd mmm yyyy HH:mm:ss zzz");
        return Lists.<Header>newArrayList(
                new BasicHeader("Date", dateFormat.format(new Date())),
                new BasicHeader("Server",
                        /* Data below is header info of my server */
                        "Apache/1.3.42 (Unix) mod_ssl/2.8.31 OpenSSL/0.9.8e")
        );
    }

    /**
     * returns the fake content using resource file in res/raw. fake_res_foo.txt is used for
     * request to http://example.com/foo
     */
    private HttpEntity createEntity(Request request) throws UnsupportedEncodingException {
        String resourceName = constructFakeResponseFileName(request);
        int resourceId = context.getResources().getIdentifier(
                resourceName, "raw", context.getApplicationContext().getPackageName());
        if (resourceId == 0) {
            Log.w(LOGGER_TAG, "No fake file named " + resourceName
                    + " found. default fake response should be used.");
        } else {
            InputStream stream = context.getResources().openRawResource(resourceId);
            try {
                String string = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
                return new StringEntity(string);
            } catch (IOException e) {
                Log.e(LOGGER_TAG, "error reading " + resourceName, e);
            }
        }

        // Return default value since no fake file exists for given URL.
        if (request instanceof StringRequest) {
            return new StringEntity(DEFAULT_STRING_RESPONSE);
        }
        return new StringEntity(DEFAULT_JSON_RESPONSE);
    }

    /**
     * Map request URL to fake file name
     */
    private String constructFakeResponseFileName(Request request) {
        String reqUrl = request.getUrl();
        String apiName = reqUrl.substring(URL_PREFIX.length());
        return "fake_res_" + apiName;
    }
}

要使用 FakeHttpStack,您只需将它传递给您的 RequestQueue。我也覆盖了 RequestQueue。

public class FakeRequestQueue extends RequestQueue {
    public FakeRequestQueue(Context context) {
        super(new NoCache(), new BasicNetwork(new FakeHttpStack(context)));
    }
}

这种方法的好处在于,它不需要对您的代码进行太多更改。您只需在测试时将您的 RequestQueue 切换为 FakeRequestQueue。因此,它可以用于验收测试或系统测试。

另一方面,对于单元测试,可能有更紧凑的方式。例如,您可以将 Request.Listener 子类实现为单独的类,以便可以轻松测试 onResponse 方法。我建议您详细说明您要测试的内容或放置一些代码片段。

【讨论】:

  • 您可以扩展 Volley.java 并覆盖 newRequestQueue() 以提供 FakeRequestQueue。如果您使用依赖注入,您可以简单地注入您的 MyVolley.java 而不是默认的 Volley.java,这样您的应用程序代码就不需要任何更改来进行测试。
  • 你能指导我如何模拟单元测试 volley 请求的响应吗?
  • 您好,我已经实现了 fakeRequestQueue,但它没有给出任何响应。看看这里stackoverflow.com/questions/32623129/…
  • 很好的解决方案。我没有覆盖 RequestQueue,但它对我来说仍然很有效。非常感谢。
【解决方案2】:

查看 volley tests 文件夹,您可以在其中找到示例。

MockCache.java
MockHttpClient.java
MockHttpStack.java
MockHttpURLConnection.java
MockNetwork.java
MockRequest.java
MockResponseDelivery.java

【讨论】:

    【解决方案3】:

    不是 100% 确定我理解您想要做什么,但如果我理解了,那么 easymock(一个允许创建模拟类的库,您可以调用并接收预定的响应)将帮助您解决很多。一个叫 Lars Vogel 的人有一篇关于这个主题的不错的文章,我在不久前使用它时发现它很有用。

    http://www.vogella.com/articles/EasyMock/article.html

    【讨论】:

      【解决方案4】:

      这是@Dmytro 提到的the current volley's MockHttpStack 的副本

      package com.android.volley.mock;
      import com.android.volley.AuthFailureError;
      import com.android.volley.Request;
      import com.android.volley.toolbox.HttpStack;
      import org.apache.http.HttpResponse;
      import java.io.IOException;
      import java.util.HashMap;
      import java.util.Map;
      public class MockHttpStack implements HttpStack {
          private HttpResponse mResponseToReturn;
          private IOException mExceptionToThrow;
          private String mLastUrl;
          private Map<String, String> mLastHeaders;
          private byte[] mLastPostBody;
          public String getLastUrl() {
              return mLastUrl;
          }
          public Map<String, String> getLastHeaders() {
              return mLastHeaders;
          }
          public byte[] getLastPostBody() {
              return mLastPostBody;
          }
          public void setResponseToReturn(HttpResponse response) {
              mResponseToReturn = response;
          }
          public void setExceptionToThrow(IOException exception) {
              mExceptionToThrow = exception;
          }
          @Override
          public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                  throws IOException, AuthFailureError {
              if (mExceptionToThrow != null) {
                  throw mExceptionToThrow;
              }
              mLastUrl = request.getUrl();
              mLastHeaders = new HashMap<String, String>();
              if (request.getHeaders() != null) {
                  mLastHeaders.putAll(request.getHeaders());
              }
              if (additionalHeaders != null) {
                  mLastHeaders.putAll(additionalHeaders);
              }
              try {
                  mLastPostBody = request.getBody();
              } catch (AuthFailureError e) {
                  mLastPostBody = null;
              }
              return mResponseToReturn;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-02-22
        • 1970-01-01
        • 2015-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多