0

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

或者

@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)

没有模拟,它正在工作。但是测试执行时间很长。这就是我想使用 Mockito 的原因:提高测试速度并避免连接问题或服务器错误。我只想测试位置是否显示在mStudioBoastPreviewFragment.getGeolocTextView().

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

谢谢您的帮助!

4

1 回答 1

2

不幸的是,我对 Android 了解不多,但我对 Mockito 了解一些。

我必须问的一个问题是:

您的 StudioBoastPreviewFragment 如何实际获取 LocationTask 的实例?从您的测试代码中,我只能猜测您的模拟 LocationTask 没有注入 - mStudioBoastPreviewFragment 似乎是您的 Test 类的成员,而 mockLocationTask 在方法范围内声明和实例化,所以我无法想到您的mStudioBoastPreviewFragment 实际上正在使用您的模拟。

然后是 Mocks 的问题:

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

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

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

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

于 2013-05-28T15:00:45.173 回答