I would like to know how can I create unit tests for the volley framework. Mock the requests, the responses so that I can create unit tests that wont require a webservice to
Here's a copy of the current volley's MockHttpStack mentioned by @Dmytro
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; } }
Not 100% sure that I understand what you want to do, but if I do, then easymock (a lib that allows the creation of mock classes, that you can make calls to and receive predetermined responses) will help you out a lot. A guy called Lars Vogel has a nice article on this topic that I found useful a while back when I used it.
http://www.vogella.com/articles/EasyMock/article.html
I implemented a subclass of HttpStack named FakeHttpStack that load fake response body from local file located in res/raw. I did this for development purpose, i.e., I can develop something for new API before server is ready, but you may learn something (e.g., overriding HttpStack#peformRequest and createEntity) from here.
/**
* 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;
}
}
To use FakeHttpStack, you just have to pass it to your RequestQueue. I override RequestQueue too.
public class FakeRequestQueue extends RequestQueue {
public FakeRequestQueue(Context context) {
super(new NoCache(), new BasicNetwork(new FakeHttpStack(context)));
}
}
Good point for this approach is that, it doesn't require much change in your code. You just have to switch your RequestQueue to FakeRequestQueue when testing. Thus, it can be used in acceptance testing or system testing.
On the other hand, for unit testing, there might be more compact way. For instance, you can implement your Request.Listener subclass as separate class so that onResponse method can be easily tested. I recommend you to put more detail about what you want to test or put some code fragment.
Take a look at volley tests folder, there you can find examples.
MockCache.java
MockHttpClient.java
MockHttpStack.java
MockHttpURLConnection.java
MockNetwork.java
MockRequest.java
MockResponseDelivery.java