Mocking Android Async Tasks with Mockito

2,046 views
Skip to first unread message

Vineeth B S

unread,
Feb 17, 2014, 12:53:08 PM2/17/14
to moc...@googlegroups.com
I am spying on an object of class DownloadTask(which is an android async task). In the spied object, I am calling methods downloadTask.execute(url); These methods help me execute the "doInBackground()" and "onPostExecute()" of the downloadTask object. Inside the doInBackground(), I am creating a "HttpURLConnection" object, to which I pass a url(kinda like a factory method), On the returned object, In the test, I am "verifying" if "getInputStream" is getting called on it. I see from the test that I have written that I am "verifying" if the method is called on a different object(hence the failure). Can you please suggest me a way to mock the method, so that I can "verify" on the appropriate object. I have attached Code snippets below:


DownloadTaskTest.Java
=============================================================
@Test
public void execute_shouldOpenInputStreamOfConnection() throws IOException{
    HttpURLConnection connectionMock = setMockConnection();
    downloadTask.execute("http://www.google.com");   
    Mockito.verify(connectionMock).getInputStream();
}
private HttpURLConnection setMockConnection() throws IOException {
    HttpURLConnection connectionMock = Mockito.mock(HttpURLConnection.class);
    Mockito.doReturn(connectionMock).when(downloadTask).createConnection(Mockito.any(URL.class));
    return connectionMock;
}
---------------------------------------------------------------------------------------------------------------------------

DownloadTask.Java
==============================================================
public class DownloadTask extends AsyncTask<String, Integer, String> {
protected String doInBackground(String... params) {
    URL url = null;
    InputStream input = null;
    OutputStream output = null;
    HttpURLConnection connection = null;
    try {
        url = new URL(params[0]);
        connection = this.createConnection(url);
        input = connection.getInputStream();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e1) {
        e1.printStackTrace();
    }
    public HttpURLConnection createConnection(URL url) throws IOException {
    HttpURLConnection connection;
    connection = (HttpURLConnection) url.openConnection();
    return connection;
}
}
}

Thanks
Vineeth BS

Eric Lefevre-Ardant

unread,
Feb 18, 2014, 7:05:43 AM2/18/14
to moc...@googlegroups.com

I *think* that what's going on is that the code explicitly refers to the spied object, not the spy itself:
  this.createConnection(url)

I'm quite sure that this will cause a call to the actual createConnection() method, and not the stubbed version.

Maybe you could try removing the "this" and see if it works any better?

--
You received this message because you are subscribed to the Google Groups "mockito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mockito+u...@googlegroups.com.
To post to this group, send email to moc...@googlegroups.com.
Visit this group at http://groups.google.com/group/mockito.
For more options, visit https://groups.google.com/groups/opt_out.

Vineeth B S

unread,
Feb 19, 2014, 8:19:04 AM2/19/14
to moc...@googlegroups.com
Dear Eric,

Thanks for looking into this.
I tried you suggestion of removing the 'this' from the class being tested.
But even still it behaves the same way. The original method is being called instead of the stub being called.

I would be really grateful if you could elaborate on the statement of 'spied object' and 'spy itself'. 

Do you think this might be happening because of us using the Robolectric runner to run these tests ?
If you have some suggestions about testing Async tasks with Robolectric runner, It would be very helpful for me.

PS: I have posted the same question on the Robolectric mailing list. I'm yet to receive an update/response from them 
--
Regards
Vineeth B S

Eric Lefevre-Ardant

unread,
Feb 28, 2014, 8:03:24 AM2/28/14
to moc...@googlegroups.com
Vineeth,

Sorry, I was completely wrong last week regarding the "this" statement. I guess my brain wasn't working well while on holidays. In truth, "this" is implicit anyway, so it cannot make any difference, whether you leave it or not.

So, I've been able to write up the same code (I think) and it does seem to work for me:
public class DownloadTaskTest
{
    @Test
    public void execute_shouldOpenInputStreamOfConnection() throws IOException
    {
        DownloadTask spy = spy(new DownloadTask());
        HttpURLConnection connectionMock = mock(HttpURLConnection.class);
        doReturn(connectionMock).when(spy).createConnection(any(URL.class));

        spy.execute("http://www.google.com");

        verify(connectionMock).getInputStream();
    }

    public static class DownloadTask
    {
        public InputStream execute(String url) throws IOException
        {
            return createConnection(new URL(url)).getInputStream();
        }

        public HttpURLConnection createConnection(URL url) throws IOException
        {
            return (HttpURLConnection) url.openConnection();
        }
    }
}


Now, of course, you'll notice that I am spying on the DownloadTask myself. I suppose that, depending on how you instantiate your DownloadTask, and how you spy it, it could work differently (although I don't see how).

I was not aware of Robolectric, but from I read on their website, I don't see how it would have an impact on spies or mocking. Have you tried running your test without it? From the snippet you have given, it does not seem to be necessary for running the test.

Reply all
Reply to author
Forward
0 new messages