Jim Clermonts
Jim Clermonts

Reputation: 2660

How to unit test AsyncTask in android

Consider below code. How can I test this without using third party libraries? The Assert line is never executed, because it is a different thread and the vm stops running. Many thanks!

public class FileParserTask extends AsyncTask<File, Void, ArrayList<City>> {

    private FileParserResult mResult;

    public interface FileParserResult {
        void onFinish(ArrayList<City> cities);
    }

    public FileParserTask(final FileParserResult result) {
        mResult = result;
    }

    @Override
    protected ArrayList<City> doInBackground(File... files) {
        ArrayList<City> cities = new ArrayList<>();
        try {
            InputStream is = new FileInputStream(files[0]);
            JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
            reader.beginArray();
            while (reader.hasNext()) {
                City city = new Gson().fromJson(reader, City.class);
                cities.add(city);
            }
            reader.endArray();
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Collections.sort(cities, (o1, o2) -> o1.getName().compareTo(o2.getName()));
        mResult.onFinish(cities);
        return cities;
    }
}

Test code:

@RunWith(AndroidJUnit4.class)
public class CityServiceTest {

    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "cities-medium.json");

    @Test
    public void givenInputAbuThenIShouldGetXResults() throws InterruptedException {
        new FileParserTask(cities -> {
            Assert.assertEquals("Input should give back 200 results", 3, cities.size());
        }).execute(file);
    }
}

Upvotes: 3

Views: 3359

Answers (3)

Jhilton
Jhilton

Reputation: 405

Sorry, did you override the onPostExecute method of the AsyncTask. You are keeping the Result handler, but not using it anywhere.

@Override
protected void onPostExecute(Object result) {
    mResult.processFinish(result);
}

As for the assertion it looks good to me as it is.

Upvotes: 1

codebrane
codebrane

Reputation: 4630

Although the code you need to test:

Assert.assertEquals("Input should give back 200 results", 3, cities.size());

is being run in an AsyncTask, that's not really relevant to unit testing. AsyncTask has most likely been extensively tested by Google so you know that it will work as an AsyncTask. The real testing seems to be the functionality that needs to be run in the background, i.e. the business logic contained in doInBackground.

Thinking about it in terms of business logic, there is a need to populate an ArrayList<City> and propagate it to the app. Android prefers this to be done on a background thread and propagation can be handled by notifications etc, both of which have been tested and released as working by Google so you don't really need to include them in a unit test. How you populate ArrayList<City> is the real unit test.

AsyncTask would be relevant for an integration test but you'd most likely be testing a different aspect of the app for that, i.e. what it displays rather than what it receives from a background thread.

So for a unit test I'd refactor out the code in doInBackground so that it can be tested independently of how Android wants it to be run.

Upvotes: 4

df778899
df778899

Reputation: 10931

As you say, the problem is the AsyncTask running in a background thread, via an ExecutorService. Like with a Future though, it provides a get() method that will wait for, and return, the result.

new FileParserTask(cities -> {
    Assert.assertEquals("Input should give back 200 results", 3, cities.size());
}).execute(file).get();

Upvotes: 0

Related Questions