如何使用Mockito进行AsyncTask模拟?

时间:2013-05-28 12:56:27

标签: android junit android-asynctask mockito

我正在尝试改进我所做的一些单元测试,所以我使用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。

感谢您的帮助!

1 个答案:

答案 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是个好主意。

另一方面,如果你想测试两个组件之间的相互作用,那么这不再是一个单元测试,而是一个完全不同的故事。