Instrumentation测试进阶

作者: changer0 | 来源:发表于2018-04-14 14:18 被阅读13次

    本篇主要讲解通过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测试进阶

        本文链接:https://www.haomeiwen.com/subject/bnllkftx.html