2016-11-24 145 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 **怎么样? –