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 …

如图所示:

《Instrumentation测试进阶》 创建测试覆盖率报告

现在你可以为代码覆盖率创建进行运行时配置. 点击运行将会生成报告.

《Instrumentation测试进阶》 配置测试覆盖率报告
《Instrumentation测试进阶》 运行测试覆盖率

生成结果:

《Instrumentation测试进阶》 测试覆盖率报告
《Instrumentation测试进阶》 敲行代码再睡觉

    原文作者:changer0
    原文地址: https://www.jianshu.com/p/a897c1b4c628
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞