2013-05-28 146 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