2013-05-28 147 views
0

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

我已经尝试了几件事,但它仍然无法正常工作。我的测试仍然给我一个断言失败:

如何执行的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) 

没有嘲讽,它的工作。但是这个测试的执行时间很长。这就是为什么我想要使用Mockito:提高测试速度并避免连接问题或服务器错误。我只想测试该位置是否显示在mStudioBoastPreviewFragment.getGeolocTextView()中。

在这两种情况下,我有一个声明失败,所以我想知道哪种方法是最好的,我怎么可以用它来嘲笑这个doInBackground()。或者如果有另一种方法模拟AsyncTask。

感谢您的帮助!

+0

我不确定是否正确理解您的测试。你想要测试什么课程? “mStudioBoastPreviewFragment”还是LocationTask?我看不到你的模拟LocationTask如何被注入到“mStudioBoastPreviewFragment”中。从所有我可以看到你的模拟LocationTask是根本不使用。 – joerx

+0

@joerx mStudioBoastPreviewFragment是一个包含我想测试的TextView的片段。此片段是名为StudioActivity的Activity的一部分,LocationTask是StudioActivity的内部类。当我点击StudioBoastPreviewFragment中的ToggleButton时,这将启动AsyncTask,LocationTask,它将geoloc用户并在我之前提到的TextView中显示其位置。所以要回答你的问题:我正在测试的类是StudioBoastPreviewFragment。 – GiyommStarsky

回答

2

我不太了解Android,但我对Mockito有一点了解。

一个问题我要问的是:

请问你StudioBoastPreviewFragment实际上获得您LocationTask的实例?从你的测试代码中,我可以猜到,你的模拟LocationTask没有被注入 - mStudioBoastPreviewFragment似乎是你的Test类的成员,而mockLocationTask是在方法范围内声明和实例化的,所以我无法想象你的方式mStudioBoastPreviewFragment实际上是使用你的模拟。

再就是用嘲弄的问题:

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

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

如果你真的想单元测试,你应该分别测试两个类,隔离任一类中的潜在错误。因此,如果正确完成,嘲笑LocationTask是一个好主意。

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

+0

我的doInBackground()这样调用: - 在我的测试用例中,我通过执行单击按钮geoloc按钮: :''geoloc.setOnCheckedChangeListener(((StudioActivity)getActivity()));' - 此侦听器在StudioActivity中调用执行的方法:'LocationTask task = new LocationTask(); task.execute((LocationManager)...);' - 等'doInBackground()'被调用... – GiyommStarsky

+0

然后你实际需要存根的是LocationTask.execute() - 像这样:doReturn (......)。当(LocationTask).execute(...)。 doInBackground()被调用的事实是LocationTask的实现细节,只要您测试ViewFragment,就不应该担心。 – joerx

+0

刚刚在代码中看到了另一个问题:看起来在你的监听器中,你使用“New LocationTask()” - 如果你创建了你的依赖项的新实例_inside_被测试的类,你根本不能嘲笑这个依赖项没有办法真正告诉你的班级使用模拟而不是真实的对象。换句话说,你的联结太紧张了。这是一个设计问题。您应该尝试找到注入LocationTask或将任务的内部逻辑移动到别处的方法,例如一些服务和嘲笑那一个。 – joerx