Android 单元测试(四)测试 Activity和Fragment

来源:互联网 发布:java编写宿舍管理系统 编辑:程序博客网 时间:2024/06/15 06:10

概述

Activity 测试的 api 基类是 InstrumentationTestCase(android.test 包内) ,这个基类提供了以下功能

生命周期控制:通过 Instrumentation 环境下提供的方法,你可以轻松的 start pause destory 一个 activity,。
依赖注入:Instrumentation 允许你创建 mock 对象,例如Context对象,Application 对象。这使你的环境和系统提供的环境分离,你也可以通过 intent 启动一个 activity。
用户交互:在 Instrumentation 下,你可以使用任何 touch 事件和键盘事件

主要实现类

ActivityInstrumentationTestCase2

The ActivityInstrumentationTestCase2 test case class is designed to do functional testing of one or more Activities in an application, using a normal system infrastructure. It runs the Activities in a normal instance of the application under test, using a standard system Context. It allows you to send mock Intents to the activity under test, so you can use it to test an activity that responds to multiple types of intents, or an activity that expects a certain type of data in the intent, or both. Notice, though, that it does not allow mock Contexts or Applications, so you can not isolate the test from the rest of a production system.

这个类的设计主要是用来多个 activity 之间的功能性测试,它使用正常的公共环境设置。你可以通过正常的上下文对象(非模拟)启动一个activity。所以,这类测试的运行是没有和实际设备的环境隔离的。

ActivityUnitTestCase

The ActivityUnitTestCase test case class tests a single activity in isolation. Before you start the activity, you can inject a mock Context or Application, or both. You use it to run activity tests in isolation, and to do unit testing of methods that do not interact with Android. You can not send mock Intents to the activity under test, although you can call Activity.startActivity(Intent) and then look at arguments that were received.

这个类将你的 activity 测试和其他系统关系隔离。在你启动一个 activity 之间,你可以注入一个 mock 的 Context 或者 Application 对象,然后利用它七佛那个一个 activity,然后在与Android环境隔离之下测试一些行为。你可以通过 Activity.startActivity(Intent )启动一个 activity,而不是创建一个 mock 的Intent 对象。

SingleLaunchActivityTestCase

The SingleLaunchActivityTestCase class is a convenience class for testing a single activity in an environment that doesn’t change from test to test. It invokes setUp() and tearDown() only once, instead of once per method call. It does not allow you to inject any mock objects.

这个类主要用来测试 launchMode 为singleTask 的 activity。

This test case is useful for testing an activity that runs in a mode other than standard. It ensures that the test fixture is not reset between tests. You can then test that the activity handles multiple calls correctly.

配置测试环境

编写测试代码

编写一个测试类继承 ActivityTestCase
可能需要重写 setUp() 方法,在这个方法完成测试之前的初始化操作,重写 tearDown() 方法,在这个方法里面做销毁回收操作。
编写testXXXXX()方法,这个testXXXX()方法就是我们的测试方法,一定要以 test 作为前缀,不然测试框架没办法识别。然而在 test 系列方法中,你可使用 assert 的所有方法

代码示例,测试代码

package com.wbiao.whistle.work;import android.app.Activity;import android.test.ActivityInstrumentationTestCase2;import android.test.suitebuilder.annotation.LargeTest;import android.widget.Button;import android.widget.TextView;/** * Created by kotlon on 2015/8/11. * */public class TestEventBusActivity extends ActivityInstrumentationTestCase2<EventBusActivity>{private Activity eventBusActivity;private Button button;private TextView textView;public TestEventBusActivity() {    super("com.wbiao.whistle.work",EventBusActivity.class);}//@Overrideprotected void setUp() throws Exception {    super.setUp();    eventBusActivity = getActivity();    button = (Button) eventBusActivity.findViewById(R.id.but_eventbus);    textView = (TextView) eventBusActivity.findViewById(R.id.tv_show);    eventBusActivity.runOnUiThread(new Runnable() {        @Override        public void run() {            button.requestFocus();            button.performClick();        }    });}// 所有的测试方法必须以 test 开头,不然 Android Instrumented Unit test 无法识别@LargeTestpublic void testperClick(){  /*         Activity eventBusActivity;     final  Button button;     final TextView textView;    eventBusActivity = getActivity();    button = (Button) eventBusActivity.findViewById(R.id.but_eventbus);    textView = (TextView) eventBusActivity.findViewById(R.id.tv_show);*/    eventBusActivity.runOnUiThread(new Runnable() {        @Override        public void run() {            button.setText("wo shi");            for(int i = 0;i<150; i++) {                //button.requestFocus();                button.performClick();            }            textView.setText("finished");        }    });    //eventBusActivity.ru //        eventBusActivity.finish();//        setActivity(null);    //int kol = eventBusActivity.num    //assertEquals(150,eventBusActivity.numberPrss);   // assert    assertNotNull(button);    assertEquals("inital", textView.getText().toString());} /*@Overrideprotected void tearDown() throws Exception {    super.tearDown();}*/}

被测试代码:

package com.wbiao.whistle.work;import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;import com.wbiao.Event.TextReleaseEvent;import java.lang.reflect.Method;import java.util.concurrent.ArrayBlockingQueue;import de.greenrobot.event.EventBus;public class EventBusActivity extends Activity implements View.OnClickListener{private Button butPress;private TextView tvShow;public static  int numberPress = 0;private AsyncTask asyncTask;private ArrayBlockingQueue arrayBlockingQueue;@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_event_bus);    EventBus.getDefault().register(this);    initView();}private void initView() {    butPress= (Button) findViewById(R.id.but_eventbus);    tvShow = (TextView) findViewById(R.id.tv_show);    butPress.setOnClickListener(this);    numberPress=0;    Method  [] methods=EventBusActivity.class.getMethods();    for(int i=0;i<methods.length;i++){            Log.v("kotlonM",methods[i].getName()+"--->"+methods[i].getModifiers());    }}@Overridepublic void onClick(View v) {    switch (v.getId()){        case R.id.but_eventbus:            numberPress++;            Toast.makeText(this,"press"+numberPress,Toast.LENGTH_LONG).show();           EventBus.getDefault().post(new TextReleaseEvent(numberPress,"i,m title"+numberPress,"i'm content"+numberPress));            break;    }}// 实际上却不会给出自动的代码提示public void onEventMainThread(TextReleaseEvent event){    tvShow.setText(event.getTitle()+event.getContent());}public int getNumberPress(){    return numberPress;}}

实际上我们要考虑的问题是,单元测试究竟能给 Activity 测试带来怎样的好处?

我大致的总结一下,有以下几点:

  1. 测试 Activity 的布局是初始化是否正确 (好像意义不大)
  2. 充当数据源,测试 activity 触发事件行为之后的准确性,例如需要从网络下载数据,但是网络接口还没有,你可以在测试代码中添加数据,对activity 的数据行为反应进行测试。
  3. 测试 activity 的交互动作是否正确相应。

三种注解,你可以在你的 testXXX()方法前面加如下注解,表示此方法可以大概执行多长时间

  • @SmallTest
    Marks a test that should run as part of the small tests. 100 毫秒以下
  • @MediumTest

    Marks a test that should run as part of the medium tests.

  • @LargeTest
    Marks a test that should run as part of the large tests.

关于 Activity 测试 api 介绍

测试 Activity 的基类是 InstrumentationTestCase ,我们可以在测试代码中

  • 生命周期控制:你可以使用Instrumentation来调用Activity的onResume、onPause、onDestroy等方法, 从而控制它的生命周期。
  • 依赖注入:你可以使用Instrumentation制造出伪Context或伪Application,这样能够帮助你更好地控制测试环境,通过自定义Intent启动Activity。
  • 用户界面交互:你可以使用Instrumentation直接发送按键信息和触屏事件。

ActivityInstrumentationTestCase2

这个类可以用来测试同一个应用的多个Activity,它拥有正常的应用实例环境和Context,可以接触到正常的系统结构(文件、数据库等)。你可以发送伪装的Intent给Activity,测试你的程序是否对接收到的各种Intent进行了正确处理。 注意:这个类中不能伪Context和伪Application,所以你无法将测试从系统环境中独立出来。

ActivityUnitTestCase

这个类是用来测试单个Activity的。它可以运行在独立的测试环境中,你可以通过在启动Activity前设置Context或Application(当然都设置也可以)来实现模拟环境。你可以在自己的虚拟环境中测试程序而不会对系统、文件等造成实际的影响。在这个测试类下你无法向正在测试的Activity发送Intent,不过你可以在启动Activity的时候调用Activity.startActivity(Intent)来查看接收到的参数。

Activity测试中的判断语句

ViewAsserts定义了供View使用的一些判断语句,它可以检查View内容的位置、对其情况和状态。

0 0