2016-12-14 93 views
0

我正在使用MVP架構來構建我的應用程序。我的演示者調用一個DataManager,負責從網絡或數據庫獲取數據。當我使用RxJava時,我訂閱了Presenter中的Observers並將適當的狀態傳遞給UI。我的服務層有Android上下文,它也會創建一個我自己類型的異常,它也引用了上下文。如何在投擲異常內模擬Crashlytics靜態方法

if (isNetworkConnected()) { 
        final Call<ServiceResponse<AppVersion>> call = mService.getAppVersion(); 
        try { 
         final Response<ServiceResponse<AppVersion>> response = call.execute(); 
         if (response.isSuccessful()) { 
          final ServiceResponse<AppVersion> serviceResponse = response.body(); response.body(); 
          if (serviceResponse.isSuccess()) { 
           subscriber.onNext(serviceResponse.getData()); 
          } else { 
           subscriber.onError(new CustomException(mContext, response.code(), response.message(), serviceResponse.getErrorList())); 
          } 
         } else { 
          subscriber.onError(new CustomException(mContext, response.code(), response.message(), response.errorBody().string())); 
         } 
        } catch (IOException e) { 
         e.printStackTrace(); 
         subscriber.onError(e); 
        } finally { 
         subscriber.onCompleted(); 
        } 
       } else { 
        subscriber.onError(new NoInternetException()); 
       } 

我的CustomException還將崩潰記錄在Crashlytics中。當我單元測試這段代碼時,Crashlytics沒有初始化異常。所以我需要模擬Crashlytics的靜態方法logException。但是,作爲主持人不接受這個對象,我該如何傳遞這個模擬對象呢?

public staticErrorType getErrorType(Throwable throwable) { 
     //409: Not handled as its a conflict response code and comes in PUT/POST 
     if (throwable instanceof IOException) { 
      return ErrorType.NO_INTERNET; 
     } else if (throwable instanceof CustomException) { 
      final int errorCode = ((CustomException) throwable).mErrorCode; 
      if (errorCode == 404) { 
       return ErrorType.NOT_FOUND; 
      } else if (errorCode == 401) { 
       return ErrorType.UNAUTORISED; 
      } else if (errorCode == 403) { 
       return ErrorType.FORBIDDEN; 
      } else if (errorCode == 500 || errorCode == 502) { 
       return ErrorType.NO_SERVER_TRY_AGAIN; 
      } else if (errorCode > 500 && errorCode < 599) { 
       return ErrorType.NO_SERVER_TRY_LATER; 
      } else if (errorCode == 1000) { 
       return ErrorType.NO_COURSE_ENROLLED; 
      } else if (errorCode == 1001) { 
       return ErrorType.NO_COURSE_STARTED; 
      } 
     } 
     if (throwable != null) { 
      Crashlytics.logException(throwable); 
     } 
     return ErrorType.SOME_THING_WENT_WRONG; 
    } 
+0

這聽起來像是你的類的設計問題。 CustomException本身不應該記錄到Crashlyrics。相反,如果有必要,應該由訂戶負責記錄。 –

+0

@DavidRawson因此自定義異常應該由其他人訂閱,或者我應該將自定義記錄器類傳遞給異常,將異常記錄到任何平臺上,例如Crashlytics,並將其用於測試? –

+0

感謝您的編輯 - 讓問題更清晰。一個好的解決方案是使用包裝類來包裝Crashlytics中的靜態方法。然後傳入包裝類作爲訂閱者的依賴關係。看[這個答案](http:// stackoverflow。com/a/29841824/5241933) –

回答

2

你遇到的是什麼some argue是靜態方法的問題。靜態方法logException()似乎無處不在,但它實際上隱藏了真正的依賴關係。由於該類需要運行該依賴關係,因此這使得您的類難以測試。

一個好的解決方案是創建一個包含非靜態方法的包裝類。如果我們在this answer將解決方案應用於你的代碼,它會是這個樣子:

public class CrashLyticsWrapper { 
    public CrashLyticsWrapper() {} 

    public void logException(Throwable t) { 
     CrashLytics.logException(t); 
    } 
} 

這然後可以在構造函數傳遞作爲一個依賴於需要它的類。由於它是一個非靜態的依賴關係,並且您現在可以控制一個類,所以您可以輕鬆地對其進行嘲弄並在必要時進行驗證。

單獨的問題:異常類似於值對象,在大多數情況下不應該具有靜態或非靜態的依賴關係。做這樣的事情:

public CustomException extends RuntimeException() { 
    public CustomException() { 
      Crashlytics.logException(this); //don't do this! 
    } 
} 

使易碎和無法測試的代碼。你可以從構造函數中看到,你從子類異常中覆蓋了有一個原因字段,一個消息字段,這就是它。一個很好的例外情況。如果您需要添加隨例外發布的功能,則應編寫單獨的錯誤處理程序,將異常作爲數據或參數。這符合SOLID中的「單一責任」原則。

1

另一種方法是使用powermock模擬包含靜態方法的Crashlytics類。

測試發送日誌,以Crashlytics可能看起來像這個這個

@RunWith(PowerMockRunner.class) 
@PrepareForTest({Crashlytics.class}) 
@PowerMockIgnore({"javax.net.ssl.*"}) 
public class presenterTest{ 

    @Test public void testFunctionWithCrashlyticsCall() throws Exception{ 
     PowerMockito.mockStatic(Crashlytics.class); 
     ... 
     assertEquals(..) 
    } 

} 

文檔功能可以在這裏找到: https://github.com/powermock/powermock/wiki/Mockito#mocking-static-method