Android 如何使用Mockito进行异步任务模拟?

Android 如何使用Mockito进行异步任务模拟?,android,junit,android-asynctask,mockito,Android,Junit,Android Asynctask,Mockito,我试图改进我所做的一些单元测试,所以我使用mockito来模拟一些对象。所以现在我有两个问题:我是否应该尝试模拟AsyncTask,以及(如果是)如何使用这个模拟对象来调用模拟对象的doInBackground()方法 我试过几种方法,但仍然不起作用。我的测试仍然给我一个断言失败: 异步任务的执行方式(在StudioActivity.java中): ..... LocationTask task = new LocationTask(); task.execute((LocationManag

我试图改进我所做的一些单元测试,所以我使用mockito来模拟一些对象。所以现在我有两个问题:我是否应该尝试模拟AsyncTask,以及(如果是)如何使用这个模拟对象来调用模拟对象的
doInBackground()
方法

我试过几种方法,但仍然不起作用。我的测试仍然给我一个断言失败:

异步任务的执行方式(在StudioActivity.java中):

.....

LocationTask task = new LocationTask();
task.execute((LocationManager) this.getSystemService(Context.LOCATION_SERVICE));
.....

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;
    }

}
@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());
}
junit.framework.AssertionFailedError: expected:<JUnit, Location> but was:<>
at  com.c4mprod.bhost.test.TestStudioActivity.testGeolocalistationLabel(TestStudioActivity.java:255)
我的异步任务(在StudioActivity.java中):

.....

LocationTask task = new LocationTask();
task.execute((LocationManager) this.getSystemService(Context.LOCATION_SERVICE));
.....

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;
    }

}
@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());
}
junit.framework.AssertionFailedError: expected:<JUnit, Location> but was:<>
at  com.c4mprod.bhost.test.TestStudioActivity.testGeolocalistationLabel(TestStudioActivity.java:255)

失败:

.....

LocationTask task = new LocationTask();
task.execute((LocationManager) this.getSystemService(Context.LOCATION_SERVICE));
.....

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;
    }

}
@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());
}
junit.framework.AssertionFailedError: expected:<JUnit, Location> but was:<>
at  com.c4mprod.bhost.test.TestStudioActivity.testGeolocalistationLabel(TestStudioActivity.java:255)
junit.framework.AssertionFailedError:应为:但为:
位于com.c4mprod.bhost.test.TestStudioActivity.TestGeoLocalizationLabel(TestStudioActivity.java:255)
没有嘲弄,这是有效的。但是测试的执行时间很长。这就是为什么我想使用Mockito:提高测试速度,避免连接问题或服务器错误。我只想测试该位置是否显示在
mstudiobowPreviewFragment.getgeoloctView()

在这两种情况下,我都有一个断言失败,所以我想知道哪种方法是最好的,以及如何使用它来模拟这个
doInBackground()
。或者如果有其他方法来模拟异步任务


谢谢你的帮助

不幸的是,我对安卓知之甚少,但我对Mockito知之甚少

我必须问的一个问题是:

您的StudioOwFragment如何实际获取LocationTask的实例?从您的测试代码中,我所能猜测的是您的模拟LocationTask没有被注入-MstudioBowPreviewFragment似乎是您的测试类的成员,而mockLocationTask是在方法范围内声明和实例化的,因此我无法想象MstudioBowPreviewFragment实际上是如何使用您的模拟的

然后是模拟的问题:

我看到LocationTask继承自AsyncTask。doInBackground(…)是如何被呼叫的?您是在调用它,还是由AsyncTask的内部逻辑调用它(类似于Thread.start()调用的Thread.run())?请记住,Mockito创建了一个没有逻辑的哑代理对象。因此,如果您依赖于AsyncTask调用doInBackground(…)的一些内部逻辑,那么这个内部逻辑将不存在于您的模拟中

换句话说,如果这是一个单元测试,那么您并不关心是否首先调用doInBackground(…),因为您的外部客户机代码不应该依赖于AsyncTask的内部或具体的子类LocationTask。回到线程示例:您模拟了Thread.start(),而不是Thread.run()

如果您真的想进行单元测试,那么应该分别测试这两个类,隔离其中任何一个类中的潜在错误。因此,如果操作正确,模拟LocationTask是一个好主意


另一方面,如果您想测试两个组件之间的交互,那么这实际上不再是一个单元测试,而是一个完全不同的故事。

我不确定是否正确理解您的测试。你要考的是哪门课?“mstudiowFragment”还是LocationTask?我看不出你模拟的LocationTask是如何被注入“mstudioPreviewFragment”的。据我所知,您的模拟LocationTask根本没有被使用。@joerx mstudiobowPreviewFragment是一个包含我要测试的文本视图的片段。此片段是名为StudioActivity的活动的一部分,LocationTask是StudioActivity的内部类。当我在StudioBulf PreviewFragment中单击一个切换按钮时,这将启动AsyncTask LocationTask,它将地理定位用户并在我前面提到的文本视图中显示他的位置。为了回答您的问题:我正在测试的类是studiobowPreviewFragment。我的doInBackground()是这样调用的:-在我的测试用例中,我通过以下方式执行geoloc按钮:
solo.clickOnView(mstudiobowPreviewFragment.getGeoloc())-在studiovableviewFragment中,my geoloc按钮有一个OnCheck侦听器:
geoloc.setOnCheckedChangeListener(((StudioActivity)getActivity())-此侦听器在StudioActivity中调用执行以下操作的方法:
LocationTask task=new LocationTask();执行((LocationManager)…)-如此类推,
doInBackground()
被调用…然后您实际需要删除的是LocationTask.execute()-如:doReturn(…).when(LocationTask).execute(…)。调用doInBackground()的事实是LocationTask的一个实现细节,只要测试ViewFragment,您就不必担心。刚才看到代码中的另一个问题:在侦听器中,您似乎使用了“New LocationTask()”—如果您在测试的类中创建依赖项的新实例,您根本无法模拟该依赖关系,因为您无法实际告诉您的类使用模拟而不是实际对象。换句话说,耦合太紧。这是一个设计问题。您应该尝试找到一种方法来注入LocationTask或将任务的内部逻辑移到其他地方,例如某个服务并模拟该服务。