Instrumentation测试进阶
本篇主要讲解通过Instrumentation如何测试Android组件, 如何生成测试覆盖率报告等.
测试Android 组件
Activity测试
测试Activity,需要使用Android 测试支持库(Android Testing Support Library)提供的ActivityTestRule类.
这个Rule提供了单个Activity的功能测试.被测试的Activity将会在@Test和@Before标注的方法执行前启动完成,在被@After标注的方法执行完成后终止.
在测试过程中,可以通过调用ActivityTestRule#getActivity()来访问测试的Activity.
下面是是一个测试Activity的示例:
假设存在下面一个Activity, 包含一个ListView,并且填充了相应的Item数据.
public class MainActivity extends AppCompatActivity {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listview);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1,
android.R.id.text1, new String[]{"测试1", "测试2", "测试3", "测试4", "测试5"});
listView.setAdapter(arrayAdapter);
}
}
在 src/androidTest/java 目录下创建 MainActivityTest类,用于测试ListView的Item个数是否正确.下面的测试用例执行通过.
package com.lulu.androidtestdemo.instrumentation;
// more
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
private static final String TAG = "MainActivityTest";
@Rule
public ActivityTestRule<MainActivity> rule = new ActivityTestRule<MainActivity>(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(), is(5));
}
}
很多时候我们能需要配置一下Intent,用来启动Activity, 通过重写ActivityTestRule#getActivityIntent() 方法可以实现该功能,测试示例如下:
假设存在如下SecondActivity .获取到启动时的Intent并解析出Key对相应的值放在TextView中.
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
TextView viewById = (TextView) findViewById(R.id.target_text);
Intent intent = getIntent();
if (intent != null) {
String key = intent.getStringExtra("key");
if (key != null) {
viewById.setText(key);
Log.d(TAG, "onCreate: key: " + key);
}
}
}
}
通过重写ActivityTestRule#getActivityIntent()方法实现模拟返回一个intent. 该测试用例执行通过.
注意: 果使用context,请使用getTargetContext,表示当前测试Activity的Context.
package com.lulu.androidtestdemo.instrumentation;
//more
import static org.junit.Assert.*;
/**
* Created by zhanglulu on 2018/2/26.
*/
@RunWith(AndroidJUnit4.class)
public class SecondActivityTest {
@Rule
public ActivityTestRule<SecondActivity> rule = new ActivityTestRule<SecondActivity>(SecondActivity.class){
@Override
protected Intent getActivityIntent() {
InstrumentationRegistry.getTargetContext();//如果使用context,请使用getTargetContext
Intent intent = new Intent();
intent.putExtra("key", "这是一个测试奥");
return intent;
}
};
@Test
public void ensureIntentDataIsDisplayed() throws Exception{
SecondActivity activity = rule.getActivity();
View viewById = activity.findViewById(R.id.target_text);
assertThat(viewById, notNullValue());
assertThat(viewById, instanceOf(TextView.class));
String text = ((TextView) viewById).getText().toString();
assertThat(text, is("这是一个测试奥"));
}
}
Service测试
测试Service,需要使用Android 测试支持库(Android Testing Support Library)提供的ServiceTestRule类.
这个Rule提供了简化的机制,可以在测试之前和之后进行启动和关闭.它可以确保在启动(或绑定)服务时正常连接. Service的启动和绑定可以通过相应的辅助方法. 测试完成, 将会在@After标注的方法执行完之后自动停止(或解绑)服务.
**注意 : **这个Rule不支持IntentService. 因为它在onHandleIntent方法之后自动销毁.
下面是一个关于Service的测试
假设存在下面的Service.
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
Log.d(TAG, "MyService: Started");
}
@Override
public IBinder onBind(Intent intent) {
return new MyLocalBinder();
}
public class MyLocalBinder extends Binder {
public MyService getMyService() {
return MyService.this;
}
}
/**
* 测试方法
* @return
*/
public String doSomethingToReturnTest() {
return "Test";
}
}
在 src/androidTest/java 目录下创建 MyServiceTest类. 用来测试启动Service和绑定Service之后方法的调用.
/**
* Created by zhanglulu on 2018/2/26.
*/
@RunWith(AndroidJUnit4.class)
public class MyServiceTest {
@Rule
public ServiceTestRule rule = new ServiceTestRule();
@Test
public void testStartedService() throws Exception {
rule.startService(
new Intent(InstrumentationRegistry.getTargetContext(), MyService.class));
}
@Test
public void testBindService() throws Exception {
IBinder binder = rule.bindService(
new Intent(InstrumentationRegistry.getTargetContext(), MyService.class));
MyService myService = ((MyService.MyLocalBinder) binder).getMyService();
assertThat(myService.doSomethingToReturnTest(), is("Test"));
}
}
Receiver测试
Receiver可以配合Mock框架直接进行测试.
例如存在下面的Receiver, 当触发onReceive时配置Intent并启动一个Activity.
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
public static final String TEST_RECEIVER = "test_receiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive: 执行了");
if (TEST_RECEIVER.equalsIgnoreCase(intent.getAction())) {
String value = intent.getStringExtra("key");
Intent i = new Intent(context, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("key", value);
context.startActivity(i);
}
}
}
验证Receiver的启动Activity的次数和参数等.
注意: 下面使用了Mockito的内容, 可以回顾之前文章.
package com.lulu.androidtestdemo.instrumentation;
/**
* Created by zhanglulu on 2018/2/26.
*/
@RunWith(AndroidJUnit4.class)
public class MyReceiverTest {
MyReceiver mReceiver = new MyReceiver();
@Mock
public Context mContext;
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Test
public void testStartActivity() throws Exception{
// prepare data for onReceive and call it
Intent intent = new Intent(MyReceiver.TEST_RECEIVER);
intent.putExtra("key", "01234567890");
mReceiver.onReceive(mContext, intent);
assertNull(mReceiver.getResultData());
//验证Receiver的操作
ArgumentCaptor<Intent> argument =
ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).startActivity(argument.capture());
Intent receivedIntent = argument.getValue();
assertNull(receivedIntent.getAction());
assertEquals("01234567890", receivedIntent.getStringExtra("key"));
assertTrue((receivedIntent.getFlags() &
Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
}
}
Content Provider 测试
测试Content Provider需要使用ProviderTestCase2 这个类, 这个类会自动在当前的测试中初始化一个Provider和一个IsolatedContext对象. 这个Context是独立于Android系统的, 但是允许文件和数据库的访问. IsolatedContext对象的作用就是确保你的Content Provider 测试不会影响真实的设备.
ProviderTestCase2 也提供了 对 MockContentResolver 的访问, 可以通过getMockContentResolver()方法获取.
Loader 测试
测试Loader, 需要使用LoaderTestCase类. 未来将提供JUnit 4规则来替换这个类.
Application 测试
Application 类包含了与整个应用程序相关的逻辑, 数据和相关设置.因此为了确保应用正常运行,还是非常有必要来测试这个类的.
你可以为Application对象编写JUnit 4测试, 并在JVM上测试它. 你可以将所有的依赖关系模拟到Application 对象中.
要在Android 运行时测试 Android 的 Application 需要使用 ApplicationTestCase. 如果Google提供一个JUnit 4 的Rule就太好了, 但是很可惜目前还没有.
Android 的测试运行器(InstrumentationTestRunner) 在其初始化阶段自动创建了Application实例. 如果你在OnCreate方法中进行了异步处理, 就应该多考虑一下.
创建代码覆盖率报告
代码覆盖率报告将显示你的应用程序代码有多少被测试覆盖.创建该测试报告需要你创建一个单独的启动配置. 选中需要创建报告的包右击选择 Create Test in ...
如图所示:
创建测试覆盖率报告现在你可以为代码覆盖率创建进行运行时配置. 点击运行将会生成报告.
配置测试覆盖率报告 运行测试覆盖率生成结果:
测试覆盖率报告 敲行代码再睡觉