我正在尝试改进我所做的一些单元测试,所以我使用mockito来模拟一些对象。所以现在我有两个问题:我是否应该尝试模拟AsyncTask和(如果是)如何使用这个模拟对象来调用模拟对象的doInBackground()
方法?
我尝试过几件事,但它仍然没有用。我的测试仍然给我一个断言失败:
如何执行AsyncTask(在StudioActivity.java中):
.....
LocationTask task = new LocationTask();
task.execute((LocationManager) this.getSystemService(Context.LOCATION_SERVICE));
我的异步任务(在StudioActivity.java中):
.....
public class LocationTask extends AsyncTask<LocationManager, Void, String> {
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<Address> 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;
}
}
这就是我尝试过的。
我的测试方法(在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));
}
失败:
junit.framework.AssertionFailedError: expected:<JUnit, Location> but was:<>
at com.c4mprod.bhost.test.TestStudioActivity.testGeolocalistationLabel(TestStudioActivity.java:255)
没有Mocking,它正在发挥作用。但测试有很长时间的执行。这就是我想使用Mockito的原因:提高测试速度并避免连接问题或服务器错误。我只想测试位置是否显示在mStudioBoastPreviewFragment.getGeolocTextView()
。
在这两种情况下我都有一个断言失败,所以我想知道哪种方法最好,我怎么能用它来模拟这个doInBackground()
。或者,如果有其他方法可以模拟AsyncTask。
感谢您的帮助!
答案 0 :(得分:2)
不幸的是,我对Android知之甚少,但我对Mockito知之甚少。
我必须问的一个问题是:
您的StudioBoastPreviewFragment如何实际获取LocationTask的实例?从我的测试代码中我可以猜到,你的模拟的LocationTask没有被注入 - mStudioBoastPreviewFragment似乎是你的Test类的成员,而mockLocationTask是在方法范围内声明和实例化的,所以我无法想到你的mStudioBoastPreviewFragment实际上正在使用你的模拟。
然后是Mocks的问题:
我看到LocationTask继承自AsyncTask。 doInBackground(...)如何被调用?你在调用它,还是由AsyncTask的内部逻辑调用(类似于Thread.rart()调用的Thread.run())?请记住,Mockito创建了一个没有逻辑的哑代理对象。因此,如果你依赖于调用doInBackground(...)的AsyncTask的一些内部逻辑,那么你的mock中就不会存在这个内部逻辑。
换句话说,如果这是一个单元测试,你不关心是否首先调用doInBackground(...),因为你的外部客户端代码不应该依赖于AsyncTask的内部或你的具体子类LocationTask。回到Thread-example:你模拟了Thread.start(),而不是Thread.run()。
如果你真的想进行单元测试,你应该分别测试这两个类,隔离任何一个类中的潜在错误。因此,如果正确完成,嘲笑LocationTask是个好主意。
另一方面,如果你想测试两个组件之间的相互作用,那么这不再是一个单元测试,而是一个完全不同的故事。