2016-11-24 137 views
4

如何編寫與系統類(即Android Framework類)交互的單元測試?如何對與系統(或Android)類進行交互的單元測試方法進行單元測試

想象一下,你有這些類:

public class DeviceInfo { 
    public final int screenWidth, screenHeight; 
    public final String model; 

    public DeviceInfo(int screenWidth, int screenHeight, String deviceModel) { 
     this.screenWidth = screenWidth; 
     this.screenHeight = screenHeight; 
     this.model = deviceModel; 
    } 

} 

public class DeviceInfoProvider { 
    private final Context context; 

    public DeviceInfoProvider(Context context) { 
     this.context = context; 
    } 

    public DeviceInfo getScreenParams() { 
     DisplayMetrics metrics = new DisplayMetrics(); 
     WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
     windowManager.getDefaultDisplay().getMetrics(metrics); 
     int screenWidth = metrics.widthPixels; 
     int screenHeight = metrics.heightPixels; 
     String model= Build.MODEL; 
     DeviceInfo params = new DeviceInfo(screenWidth, screenHeight, model); 
     return params; 
    } 
} 

我怎麼能寫一個測試,以驗證該方法DeviceInfoProvider.getScreenParams()的正確行爲。

下測試通過,但它是非常醜陋的,脆弱的:

@Test 
public void testGetScreenParams() throws Exception { 
    // Setup 
    Context context = spy(RuntimeEnvironment.application); 
    DeviceInfoProvider deviceInfoProvider = new DeviceInfoProvider(context); 

    // Stub 
    WindowManager mockWindowManager = mock(WindowManager.class); 
    Display mockDisplay = mock(Display.class); 
    when(context.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mockWindowManager); 
    when(mockWindowManager.getDefaultDisplay()).thenReturn(mockDisplay); 
    doAnswer(new Answer() { 
     @Override 
     public Object answer(InvocationOnMock invocation) throws Throwable { 
      DisplayMetrics metrics = (DisplayMetrics) invocation.getArguments()[0]; 
      metrics.scaledDensity = 3.25f; 
      metrics.widthPixels = 1081; 
      metrics.heightPixels = 1921; 
      return null; 
     } 
    }).when(mockDisplay).getMetrics(any(DisplayMetrics.class)); 

    // Run 
    DeviceInfo deviceInfo = deviceInfoProvider.getScreenParams(); 

    // Verify 
    assertThat(deviceInfo.screenWidth, equalTo(1081)); 
    assertThat(deviceInfo.screenHeight, equalTo(1921)); 
    assertThat(deviceInfo.model, equalTo(Build.MODEL)); 
} 

您認爲應如何改進呢?

注:目前我使用Robolectric,和的Mockito PowerMock

+0

抽象的上下文特定的代碼,以便它可以被嘲笑單元測試。儘量避免嘲笑你不擁有的課程。 – Nkosi

+0

是'DeviceInfoProvider'的意思是用作依賴? – Nkosi

+0

的問題是:你爲什麼要測試這個?你不相信谷歌的人嗎?你應該首先對你的代碼進行單元測試,並嘲笑你不負責的依賴。 –

回答

6

被測系統過於緊密耦合的實現方面。儘量避免嘲笑你不擁有的課程。在接口後面的抽象代碼,並將責任委託給運行時發生在接口後面的任何實現。

public interface DisplayProvider { 
    public int widthPixels; 
    public int heightPixels; 
} 

public interface BuildProvider { 
    public string Model; 
} 

重構依賴類依賴於抽象而不是結核(實現方面的擔憂)。在隔離

public class DeviceInfoProvider { 
    private final DisplayProvider display; 
    private final BuildProvider build; 

    public DeviceInfoProvider(DisplayProvider display, BuildProvider build) { 
     this.display = display; 
     this.build = build; 
    } 

    public DeviceInfo getScreenParams() { 
     int screenWidth = display.widthPixels; 
     int screenHeight = display.heightPixels; 
     String model = build.Model; 
     DeviceInfo params = new DeviceInfo(screenWidth, screenHeight, model); 
     return params; 
    } 
} 

單元測試

@Test 
public void testGetScreenParams() throws Exception { 
    // Arrange 
    DisplayProvider mockDisplay = mock(DisplayProvider.class); 
    BuildProvider mockBuild = mock(BuildProvider.class);   
    DeviceInfoProvider deviceInfoProvider = new DeviceInfoProvider(mockDisplay, mockBuild); 

    when(mockDisplay.widthPixels).thenReturn(1081); 
    when(mockDisplay.heightPixels).thenReturn(1921); 
    when(mockBuild.Model).thenReturn(Build.MODEL); 

    // Act 
    DeviceInfo deviceInfo = deviceInfoProvider.getScreenParams(); 

    // Assert 
    assertThat(deviceInfo.screenWidth, equalTo(1081)); 
    assertThat(deviceInfo.screenHeight, equalTo(1921)); 
    assertThat(deviceInfo.model, equalTo(Build.MODEL)); 
} 
+0

感謝您的回答,這是一個很好的建議。但是,如果你想測試新的DisplayProvider和BuildProvider,你最後需要模擬系統類,對吧? – Addev

+0

再次,這些類將包裝實現問題,這些問題已經由供應商進行了可擴展測試。這也將離開單元測試領域並進入集成測試。 – Nkosi

+0

嘲笑** Context **怎麼樣? –