Reputation: 763
:)
I'm facing an issue simulating network exceptions using Volley. My idea is to "mock" a Network class that will always throw an exception. Then use that Network in my RequestQueue and test how my DataManager(MyManager) behaves when a network exception is thrown. I know the approach may not be fully correct, but then I wish I could knew a better way to implement my UnitTest case.
By the way, I use Robolectric 2.2 and Mockito 1.9.5. Let's dig into the code:
RequestQueue mMainRequestQueue;
private Network mMockedNetwork;
@Before
public void setup(){
Robolectric.getFakeHttpLayer().interceptHttpRequests(false);
ShadowLog.stream = System.out;
mMockedNetwork = spy(new BasicNetwork(new HurlStack()));
mMainRequestQueue = new RequestQueue(new NoCache(), mMockedNetwork);
mMainRequestQueue.start();
}
@Test
public void testLogin() throws Exception {
doThrow(new VolleyError()).when(mMockedNetwork).performRequest(any(JsonObjectRequest.class));
MyManager.getInstance(Robolectric.application.getApplicationContext()).setMainQueue(mMainRequestQueue);
MyManager.getInstance(Robolectric.application.getApplicationContext()).createSession("sessionId", "date", new MyManager.OnResponseReceivedListener() {
@Override
public void responseReceived(boolean error, Object responseObject) {
if (error) {
...
} else {
...
}
}
});
}
MyManager createSession(..)
method just creates a new instance of a JsonObjectRequest
and invokes OnResponseReceivedListener
's method whenever a response is received.
When I run the test, the callback methods (the ones in JSONObjectRequest and of course the one in the listener) are never called.
Any idea why? Am I missing something? Maybe the whole approach is wrong?
Thanks a lot people!
I've also tried replacing new RequestQueue(...)
line with this one mMainRequestQueue = new RequestQueue(new NoCache(), mMockedNetwork, 4,new ExecutorDelivery(Executors.newSingleThreadExecutor()));
Same result.
As David Wallace has mentioned, that method is called from a Volley library class Called NetworkDispatcher Here is the link
....
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
...
The networkDispatchers are created within the RequestQueue, using the "mocked" object as parameter. So I expect that method to be called.
Upvotes: 2
Views: 4261
Reputation: 79838
In your @Before
method, you start the RequestQueue
thread, which in turn calls performRequest
. Later, in your @Test
method, you stub the performRequest
method - that is, you specify what should happen when performRequest
is called.
But this creates a race condition. The behaviour of this test is indeterminate, because we can't tell whether the RequestQueue
thread will reach the call to performRequest
before or after that call is stubbed. So you might get the original version of the method, or you might get the stubbed version. The only way to be sure is to re-engineer the way the test works.
You really need to stub performRequest
FIRST, and then start your RequestQueue
thread. Probably the simplest thing to do would be to move mMainRequestQueue.start();
down into the @Test
method, after all the stubbing has been done.
Upvotes: 1