Android的单元测试不需要运行App,所有的任务都交给Robolectric和Mockito去做,简单方便。
1、Robolectric
1.1 基本设置
在Android Studio下的build.gradle添加:
testCompile "org.robolectric:robolectric:3.1"
1.2 Shadow
Robolectric定义了大量的Shadow类,修改或者扩展了Android OS类的行为。当一个Android OS类被实例化,Robolectric会搜索相应的Shadow类;如果找到了,将创建与之关联的Shadow对象。Android OS方法每次被调用时,Robolectirc确保:如果存在,Shadow类中的相应方法先被调用,这样就有机会做测试相关逻辑。这种策略可运用于所有的方法,包括static和final方法。
另外还可以自定义Shadow,为自定义类提供Shadow方法:
@Implements(ImageView.class)
public class ShadowImageView extends ShadowView {
...
@Implementation
public void setImageResource(int resId) {
// implementation here.
}
}
以上代码也可以不继承于ShadowView。为了让Robolectric知道自定义的Shadow类,需要使用@Config(shadows=ShadowImageView.class)注解测试方法或者类,或者创建一个文件org.robolectric.Config.properties
文件中包含shadows=my.package.ShadowFoo。
2、Mockito
Robolectric在文档中声称:“No Mocking Frameworks Required”:对于Robolectric的另一种可选方法是使用mock框架,比如Mockito;或者模拟出Android SDK。虽然这是个有效的方法,但基本上是应用代码的反向实现。
Mockito虽然不能模拟final类、匿名类和Java基本类型;对于final方法和static方法,不能对其 when(…).thenReturn(…) 操作。另外mock对象,大多都需要植入到应用代码中,从而进行verify(...)操作;但应用代码中不一定有相应的set方法,如果要植入,就需要为了测试添加应用代码。
因此觉得把两者结合起来,发挥各自所长,简单方便。
3、实例
@Implements(AuthManager.class)
public class ShadowAuthManager {
private static AuthManager instance = mock(AuthManager.class);
@Implementation
public boolean hasAuth(String authCode) {
return instance.hasAuth(authCode);
}
@Implementation
public synchronized static AuthManager getInstance() {
return instance;
}
}
AuthManager用于验证当前用户是否有某个权限,如上代码定义了相应的Shadow类,并在类中使用mock实例化对象,在测试时,通过when(...).thenReturn(...)返回不同值,实现不同的测试条件。
@Test
@Config(shadows = ShadowAuthManager.class)
public void should_show_toast_when_click_cashier_without_auth() throws Exception {
AuthManager mockAuthManager = AuthManager.getInstance();
when(mockAuthManager.hasAuth(...)).thenReturn(false);
View view = activity.findViewById(R.id.get_money);
view.performClick();
CalmToastView toast = (CalmToastView) ShadowToast.getLatestToast();
assertEquals(activity.getString(R.string.no_authority), toast.getMessage());
}
@Test
@Config(shadows = ShadowAuthManager.class)
public void should_goto_cashier_activity_when_click_cashier_with_auth() throws Exception {
AuthManager mockAuthManager = AuthManager.getInstance();
when(mockAuthManager.hasAuth(...)).thenReturn(true);
View view = activity.findViewById(R.id.get_money);
view.performClick();
ShadowApplication application = shadowOf(RuntimeEnvironment.application);
assertEquals("......", application.getNextStartedActivity().getComponent().getClassName());
}
参考文献:http://robolectric.org/getting-started/