关于android单元测试的一切
介绍
-
setup:即new 出待测试的类,设置一些前提条件
-
执行动作:即调用被测类的被测方法,并获取返回结果
-
验证结果:验证获取的结果跟预期的结果是一样的
Junit4
java测试框架
testCompile 'junit:junit:4.12'
@Before
@Test
(expected = IllegalArgumentException.class) 验证是否抛该异常
@After
@Ignore 跑所有测试方法忽略该方法
除了在AndroidStudio里面运行,你还可以在命令行通过gradle testDebugUnitTest,或者是gradle testReleaseUnitTest,分别运行debug和release版本的unit testing
Mockito的使用
-
参考 https://segmentfault.com/u/chriszou
java界使用最广法的mock框架
概念就是创建一个类的虚假对象,在测试环境中,用来替换掉真实的 -
添加依赖
repositories { jcenter() }
dependencies {
testCompile "org.mockito:mockito-core:
}
- api
- 验证方法调用:
Mockito.verify(objectToVerify,Mockito.times(3)).methodToVerify(Mockito.anyString());
- 设定返回值,方便测试:
Mockito.when(mockObject.targetMethod(args)).thenReturn(desiredReturnValue);
- 执行特定动作,如访问网络数据
Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args);
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//这里可以获得传给performLogin的参数
Object[] arguments = invocation.getArguments();
//callback是第三个参数
NetworkCallback callback = (NetworkCallback) arguments[2];
callback.onFailure(500, "Server error");
return 500;
}
}).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
ps: Mockito.spy与mock的唯一区别就是默认行为不一样:spy对象的方法默认调用真实的逻辑,mock对象的方法默认什么都不做,或直接返回默认值。
dagger2
能不用就不用!
- 依赖
buildscript {
...
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
//添加apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
...
//应用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
//引入dagger2
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java注解
provided 'org.glassfish:javax.annotation:10.0-b28'
}
- 依赖注入概念
我们平常使用的构造函数,需要传入对象去使用,就算是一种依赖注入,然而当我们需要的对象嵌套越来越多的时候,就会显得冗余,这个时候dagger2就出现了。
client --> Component(工厂管理员)-->Module(生产依赖的工厂)-->Dependency
-
api
Module
@Module 修饰类
@Provides 修饰方法
@Singleton 修饰方法,获取单例节省资源
Component
@Component
(modules={OneModule.class}) 修饰类,指定管理的生产Dependency的工厂
1. 定义返回Dependency的抽象方法即可
2. 先在Component 定义inject(Object obj方法
定义成员变量@Inject修饰即可
ps: 不能继承,都要实现inject(this)方法才能生成实例 -
单元测试
//需要测试
public class LoginActivity extends AppCompatActivity {
@Inject
LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ComponentHolder.getAppComponent().inject(this);
findViewById(R.id.login).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String username = ((EditText) findViewById(R.id.username)).getText().toString();
String password = ((EditText) findViewById(R.id.password)).getText().toString();
mLoginPresenter.login(username, password);
}
});
}
}
//测试
public class DaggerUtils {
public static final AppModule appModule = spy(new AppModule(RuntimeEnvironment.application));
public static void setupDagger() {
AppComponent appComponent = DaggerAppComponent.builder().appModule(appModule).build();
ComponentHolder.setAppComponent(appComponent);
}
}
//-------
@RunWith(RobolectricGradleTestRunner.class) //Robolectric相关,看不懂的话忽略
@Config(constants = BuildConfig.class, sdk = 21) //同上
public class LoginActivityTest {
@Test
public void testActivityStart() {
@Test
public void testActivityStart() {
DaggerUtils.setupDagger();
LoginPresenter mockLoginPresenter = mock(LoginPresenter.class);
Mockito.when(DaggerUtils.appModule.provideLoginPresenter(any(UserManager.class), any(PasswordValidator.class))).thenReturn(mockLoginPresenter);
LoginActivity loginActivity = Robolectric.setupActivity(LoginActivity.class);
((EditText) loginActivity.findViewById(R.id.username)).setText("xiaochuang");
((EditText) loginActivity.findViewById(R.id.password)).setText("xiaochuang is handsome");
loginActivity.findViewById(R.id.login).performClick();
verify(mockLoginPresenter).login("xiaochuang", "xiaochuang is handsome"); //pass!
}
}
}
Robolectric使用
Robolectric就是一个能够让我们在JVM上跑测试时够调用安卓的类的框架
- android.jar不完整,android相关方法只是抛出异常RuntimeException("stub!!")
- 依赖
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.2.1"```
- api
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
@Test
public void testMainActivity() {
MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);
mainActivity.findViewById(R.id.textView1).performClick();
Intent expectedIntent = new Intent(mainActivity, SecondActivity.class);
ShadowActivity shadowActivity = Shadows.shadowOf(mainActivity);
Intent actualIntent = shadowActivity.getNextStartedActivity();
Assert.assertEquals(expectedIntent, actualIntent);
}
}
- 参考: http://chriszou.com/2016/06/07/android-unit-testing-everything-you-need-to-know.html