1. 测试分类
Android端的单元测试一般分为两类:
- Local unit tests - 只需要JVM就可以运行的测试(后文简称UnitTest)
- Instrumented tests - 需要机遇Android系统环境的测试(后文简称InstrumentedTest)
InstrumentedTest依赖Andorid环境,所以一般需要借助Mockito
等框架
2. 测试工程结构
-
app/src/main/java- 业务代码
-
app/src/test/java - UnitTest代码
-
app/src/androidTest/java - InstrumentedTest代码
3. Local Unit Test
3.1 添加依赖
添加依赖如下,Andorid新建工程一般会自动添加:
dependencies {
// Unit testing dependencies
testImplementation 'junit:junit:4.12'
}
3.2 创建测试模板
测试一段温度转换的逻辑,如下:
public class ConverterUtil {
// converts to celsius
public static float convertFahrenheitToCelsius(float fahrenheit) {
return ((fahrenheit - 32) * 5 / 9);
}
// converts to fahrenheit
public static float convertCelsiusToFahrenheit(float celsius) {
return ((celsius * 9) / 5) + 32;
}
}
在被测试代码的文件中,Right click > Go To > Test
- 选择测试类型 JUnit4
- 选择是否生成
@Before
、@After
方法, 用来进行前/后处理 - 手动选择需要测试的方法,回升陈
testXXX
的模板代码 - 选择生成的测试代码路径
生产的测试模板如下:
3.3 实现测试代码
src/test
或者src/androidTest
中访问src/main
的任何代码,所以可以在上面模板中填充测试逻辑:
public class ConverterUtilTest {
@Test
public void testConvertFahrenheitToCelsius() {
float actual = ConverterUtil.convertCelsiusToFahrenheit(100);
// expected value is 212
float expected = 212;
// use this method because float is not precise
assertEquals("Conversion from celsius to fahrenheit failed", expected, actual, 0.001);
}
@Test
public void testConvertCelsiusToFahrenheit() {
float actual = ConverterUtil.convertFahrenheitToCelsius(212);
// expected value is 100
float expected = 100;
// use this method because float is not precise
assertEquals("Conversion from celsius to fahrenheit failed", expected, actual, 0.001);
}
}
3.4 启动测试
Run ‘XXXTest’ 后可以看到测试报告
3.5 JUnit4注解介绍
整个测试Case中依靠大量JUnit4的注解完成,注解的主要含义如下:
4. Android Instrumented Test
4.1 添加依赖
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
dependencies {
// Unit testing dependencies
androidTestCompile 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
// more stuff, e.g., Mockito
}
- AndroidJUnitRunner: InstrumentedTest的基础,协助在Android设备上执行JUnit测试:
- 加载测试代码和业务代码到目标设备
- 在instrumented环境中执行测试case并报告结果
- androidx.test:runner : 在Android上执行JUnit4,使用
@Runwith(AndroidJUnit4.class)
注解声明在对应TestCase上,相对于纯JUnit4来说,提供了一些Espresso测试混合ActivityTestRule时的需要。 - Espresso:espresso框架主要用来解决重复性的UI操作,例如:
@Test
public void runClick(){
onView(withText("点击获取城市感冒指数")).perform(click());
onView(withId(R.id.tv)).check(matches(withText("点击获取城市今天温度")));
}
- Mockito:三方框架,相对于Instrumented来说,可以脱离Android设备调试环境,在纯Java环境下面完成Android测试用例,但是测试的局限性加大
- fragment-testing:Android针对fragment新增的测试库,便于针对Fragment进行测试
4.2 创建测试模板
参考3.2 ,主要测试类型选择 Android Instrumentation Tests,生成位置src/androidTest/
4.3 测试代码
使用ActivityTestRule测试Activity
ActivityTestRule帮助启动一个Activity用于测试,如下
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
@Test
public void ensureListViewIsPresent() throws Exception {
MainActivity activity = rule.getActivity();
View viewById = activity.findViewById(R.id.listview);
assertThat(viewById,notNullValue());
assertThat(viewById, instanceOf(ListView.class));
ListView listView = (ListView) viewById;
ListAdapter adapter = listView.getAdapter();
assertThat(adapter, instanceOf(ArrayAdapter.class));
assertThat(adapter.getCount(), greaterThan(5));
}
}
使用Espresso测试UI
Espresson可以用来访问UI控件并进行测试,如下
public class SimpleViewTest {
@Rule
public ActivityTestRule<SimpleViewActivity> mActivityTestRule =
new ActivityTestRule<SimpleViewActivity>(SimpleViewActivity.class);
@Test
public void textViewTest() throws Exception {
onView(withId(R.id.tv_simple_view))
.check(matches(withText("111")));
}
@Test
public void buttonTest() throws Exception {
onView(withId(R.id.btn_simple_view))
.check(matches(withText("222")))
.perform(click());
onView(withId(R.id.tv_simple_view))
.check(matches(withText("333")));
}
}
使用FragmentScenario测试Fragment
AndroidX新推出的针对FragmentScenario
和 ActivityScenario
可以更简单的创建Activity和Fragment供测试代码访问,以FragmentScenario为例:
@RunWith(AndroidJUnit4::class)
class SampleFragmentTest {
@Test
fun launchFragmentAndVerifyUI() {
// use launchInContainer to launch the fragment with UI
val scenario; = launchFragmentInContainer<SampleFragment>()
scenario.onFragment{
fragment ->
// now use espresso to look for the fragment's text view and verify it is displayed
onView(withId(R.id.textView)).check(matches(withText("I am a fragment")));
}
}
}
参考
https://www.vogella.com/tutorials/AndroidTesting/article.html