I'm trying to

some unit tests I've made, so I'm using mockito to mock some objects. So for now I have two questions: Should I try to mock an AsyncTask and (if yes) how can I use this mocked object to call the doInBackground() method of the mocked object?

I've tried several things but it's still not working. My test still gives me an Assertion Failure:

How the AsyncTask is executed (in the StudioActivity.java):

.....

LocationTask task = new LocationTask();

task.execute((LocationManager) this.getSystemService(Context.LOCATION_SERVICE));

My Async Task (in the StudioActivity.java):

.....

public class LocationTask extends AsyncTask {

private LocationManager mLocationManager;

// private TextView mLocText;

@Override

protected void onPreExecute() {

super.onPreExecute();

// mLocText = (TextView) findViewById(R.id.textView_location);

// mLocText.setVisibility(View.VISIBLE);

}

@Override

public String doInBackground(LocationManager... params) {

mLocationManager = params[0];

Location loc = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

Geocoder gcd = new Geocoder(getApplicationContext(), Locale.getDefault());

List

addresses;

try {

addresses = gcd.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);

if (addresses.size() > 0) {

return (addresses.get(0).getLocality() + ", " + addresses.get(0).getCountryName());

}

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

@Override

protected void onPostExecute(String result) {

mPreviewFragment.setLocation(result);

mLocation = result;

}

}

This is what I've tried.

My Test Method (in the TestStudioActivity.java):

@UiThreadTest

public void testGeolocalistationLabel() throws InterruptedException, ExecutionException{

LocationTask mockLocationTask = mock(StudioActivity.LocationTask.class);

when(mockLocationTask.doInBackground((LocationManager[]) any())).thenReturn("JUnit, Location");

mStudioBoastPreviewFragment.getGeoloc().performClick();

assertEquals("JUnit, Location",mStudioBoastPreviewFragment.getGeolocTextView().getText());

verify(mockLocationTask).doInBackground((LocationManager[]) any());

}

OR

@UiThreadTest

public void testGeolocalistationLabel() throws InterruptedException, ExecutionException{

LocationTask mockLocationTask = mock(StudioActivity.LocationTask.class);

when(mockLocationTask.doInBackground((LocationManager) mStudioActivity.getSystemService(Context.LOCATION_SERVICE))).thenReturn("JUnit, Location");

mStudioBoastPreviewFragment.getGeoloc().performClick();

assertEquals("JUnit, Location",mStudioBoastPreviewFragment.getGeolocTextView().getText());

verify(mockLocationTask).doInBackground((LocationManager) mStudioActivity.getSystemService(Context.LOCATION_SERVICE));

}

The Failure:

junit.framework.AssertionFailedError: expected: but was:<>

at com.c4mprod.bhost.test.TestStudioActivity.testGeolocalistationLabel(TestStudioActivity.java:255)

Without Mocking, it's working. But the test has long time execution. That's why I wanted to use Mockito: to

test speed and to avoid connection problems or server errors. I just want to test if the location is displayed in the mStudioBoastPreviewFragment.getGeolocTextView().

In both case I have an Assertion Failure so I wanted to know which method is the best and how could I use it to mock this doInBackground(). Or if there is another way to Mock AsyncTask.

Thanks for the help!No matter what I try with the Gradle build file, I get a NoSuchMethodError for org.hamcrest.Matcher.anyOf when I try to run my tests after doing gradle androidTestCompile. Here is my current configuration: dependencies { compile project

android

junit

android-asynctask

mockito

|

this question

edited May 28 '13 at 13:01 asked May 28 '13 at 12:56

GiyommStarsky 26 6      I'm not sure if I understand your test correctly. What is the class you are trying to test? "mStudioBoastPreviewFragment" or the LocationTask? I can't see how your mocked LocationTask gets injected into "mStudioBoastPreviewFragment". From all I can see your mocked LocationTask is simply not used. –

joerx May 28 '13 at 13:12      @joerx mStudioBoastPreviewFragment is a fragment which contains the TextView I want to test. This fragment is part of an Activity called StudioActivity, and LocationTask is an inner class of StudioActivity. When I click on a ToggleButton in StudioBoastPreviewFragment this starts the AsyncTask, LocationTask,that will geoloc the user and display his location in the TextView I've mentioned earlier. So to answer your question: The class I'm testing is the StudioBoastPreviewFragment. –

GiyommStarsky May 28 '13 at 13:24

|

1 Answers

1

I don't know much about Android unfortunately, but I do know a bit about Mockito.

One question I must ask is:

How does your StudioBoastPreviewFragment actually obtain an instance of your LocationTask? From your test code all I can guess is that your mocked LocationTask is not injected - mStudioBoastPreviewFragment seems to be a member of your Test class, while mockLocationTask is declared and instantiated within the method scope, so there is no way I can think of how your mStudioBoastPreviewFragment is actually using your mock.

Then there's the issue with Mocks:

I see that LocationTask inherits from AsyncTask. How does doInBackground(...) get called? Are you calling it, or is this called by the internal logic of AsyncTask (similar to Thread.run() being called by Thread.start())? Keep in mind that Mockito creates a dumb proxy object with no logic in it. So, if you rely on some internal logic of AsyncTask calling doInBackground(...), this internal logic will not exist in your mock.

In other words, if this is a Unit test, you don't care if doInBackground(...) is called in the first place, as your external client code should not depend on the internals of AsyncTask or your concrete subclass LocationTask. To come back to the Thread-example: you mock out Thread.start(), not Thread.run().

If you really want to Unit-test, you should test the two classes separately, isolating potential errors in either class. Hence, mocking out LocationTask is a good idea, if done correctly.

If on the other hand you want to test the interactions between the two components, this is not really a Unit-Test anymore and a whole different story.

|

this answer answered May 28 '13 at 15:00

joerx 1,046 9 14      My doInBackground() is called this way: - In my testcase I perform the click on the geoloc button by:

solo.clickOnView(mStudioBoastPreviewFragment.getGeoloc()); - In StudioBoastPreviewFragment, my geoloc button has an OnCheck Listener:

geoloc.setOnCheckedChangeListener(((StudioActivity) getActivity())); - This listener calls a method, in StudioActivity, that does the execute:

LocationTask task = new LocationTask(); task.execute((LocationManager) ... ); - And so on the

doInBackground() is called... –

GiyommStarsky May 28 '13 at 15:43      Then what you actually need to stub out is LocationTask.execute() - like so: doReturn(...).when(LocationTask).execute(...). The fact that doInBackground() is called is an implementation detail of LocationTask you should not be concerned about as long as you test your ViewFragment. –

joerx May 28 '13 at 16:04      Just saw another problem in your code: seems in your listener, you use "New LocationTask()" - if you create a new instance of your dependency

inside the class under test, you can't mock that dependency at all, as you have no way to actually tell your class to use the mock instead of a real object. In other words, you have too tight coupling. This is a design issue. You should try to find a way to inject LocationTask or move the internal logic of the task elsewhere, e.g. some service and mock out that one. –

joerx May 28 '13 at 16:44

|

runs and @Test methods are apparently called before the injection has finished, because I get NullPointerException whenever I try to use the `LoginPresenter in my test code. @RunWith(MockitoJUnitRunner.class)@EBeanpublic class LoginPresen

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐