[关闭]
@ZeroGeek 2015-09-25T05:07:29.000000Z 字数 4991 阅读 643

Android单元测试

每周主题


什么是单元测试?(摘自百度百科)

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试

单元测试的好处?

针对Android:
每次修改代码后需要重新编译,对于复杂的App,可能花费几分钟。极大浪费了时间,因此为了减少bug,提高代码质量,单元测试是十分被需要的。

单元测试要点

1. 接口功能测试

2. 局部数据结构测试(不常用)

3. 边界条件测试

一些例子

4. 独立执行通路测试

代码覆盖率

Android中的单元测试

举例,来测试下面的Activity,在EditText中输入字符,然后点击按钮,在TextView显示输入的字符。

  1. public class FirstActivity extends Activity {
  2. private static final String TAG = "FirstActivity";
  3. @Bind(R.id.first_et)
  4. EditText mEt;
  5. @Bind(R.id.first_tv)
  6. TextView mTv;
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_first);
  11. ButterKnife.bind(this);
  12. Log.d(TAG,"onCreate First");
  13. }
  14. @OnClick(R.id.first_btn)
  15. public void setTextToView() {
  16. mTv.setText(mEt.getText().toString());
  17. }
  18. //....
  19. }

1.搭建测试环境

AndroidManifest.xml中添加权限与配置
在Application外加入

  1. <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
  2. <instrumentation
  3. android:name="android.test.InstrumentationTestRunner"
  4. android:targetPackage="com.zero.androidtranningdemo" />

在Application内加入

  1. <uses-library android:name="android.test.runner" />

之后所使用的测试库是自带的。

2. 编写测试用例(androidTest里)

建议与上面文件夹一一对应

  1. package com.zero.androidtranningdemo.activities;
  2. import android.app.Instrumentation;
  3. import android.test.ActivityInstrumentationTestCase2;
  4. import android.test.TouchUtils;
  5. import android.test.UiThreadTest;
  6. import android.test.ViewAsserts;
  7. import android.test.suitebuilder.annotation.MediumTest;
  8. import android.test.suitebuilder.annotation.SmallTest;
  9. import android.view.View;
  10. import android.widget.Button;
  11. import android.widget.EditText;
  12. import android.widget.TextView;
  13. import com.zero.androidtranningdemo.R;
  14. /**
  15. * Created by zero on 15-9-21.
  16. */
  17. public class FirstActivityTest extends ActivityInstrumentationTestCase2<FirstActivity> {
  18. // 构造函数是由测试用的Runner调用,用于初始化测试类的。
  19. public FirstActivityTest() {
  20. super(FirstActivity.class);
  21. }
  22. private Instrumentation mInstrumentation; // 我们可以将它理解为一种没有图形界面的,具有启动能力的,用于监控其他类(用Target Package声明)的工具类
  23. private FirstActivity mActivity;
  24. private TextView mTv;
  25. private EditText mEt;
  26. private Button mBtn;
  27. /**
  28. * setUp()方法是由测试Runner在其他测试方法开始前运行的。(必要)
  29. * 1.调用父类构造函数,这是JUnit要求的。
  30. * 2.初始化测试数据集的状态,具体而言:
  31. * a.定义保存测试数据及状态的实例变量
  32. * b.创建并保存正在测试的Activity的引用实例。
  33. * c.获得想要测试的Activity中任何UI组件的引用。
  34. *
  35. * @throws Exception
  36. */
  37. @Override
  38. protected void setUp() throws Exception {
  39. super.setUp();
  40. // 把touch mode设置为真可以防止在执行编写的测试方法时,人为的UI操作获取到控件的焦点
  41. // 确保在调用getActivity()方法前调用了setActivityInitialTouchMode
  42. // setActivityInitialTouchMode(true);
  43. mInstrumentation = getInstrumentation();
  44. mActivity = getActivity();
  45. mTv = (TextView) mActivity.findViewById(R.id.first_tv);
  46. mEt = (EditText) mActivity.findViewById(R.id.first_et);
  47. mBtn = (Button) mActivity.findViewById(R.id.first_btn);
  48. }
  49. /**
  50. * 测试前提,检查测试数据集的设置是否正确,以及我们想要测试的对象是否已经正确地初始化。
  51. */
  52. public void testPreconditions() {
  53. assertNotNull("mActivity is null", mActivity);
  54. assertNotNull("mTv is null", mTv);
  55. assertNotNull("mEt is null", mEt);
  56. assertNotNull("mBtn is null", mBtn);
  57. }
  58. /**
  59. * 编写的测试方法
  60. */
  61. // @UiThreadTest 可用于子线程处理
  62. @MediumTest
  63. public void testClickBtn() {
  64. final String str = "Hello";
  65. // 同步处理
  66. mInstrumentation.runOnMainSync(new Runnable() {
  67. @Override
  68. public void run() {
  69. mEt.setText(str); // 设置测试数据
  70. mBtn.requestFocus(); // 得到焦点
  71. mBtn.performClick(); // 模拟点击事件
  72. assertEquals(str, mTv.getText().toString()); // 判断结果
  73. }
  74. });
  75. // 如果不是Btn,其它View的点击可使用TouchUtils
  76. // TouchUtils.clickView(this, mBtn);
  77. }
  78. /**
  79. * 注意:TouchUtils辅助类提供与应用程序交互的方法可以方便进行模拟触摸操作。
  80. * 我们可以使用这些方法来模拟点击,轻敲,或应用程序屏幕拖动View。
  81. * <p/>
  82. * 警告:TouchUtils方法的目的是将事件安全地从测试线程发送到UI线程。
  83. * 我们不可以直接在UI线程或任何标注@UIThread的测试方法中使用TouchUtils这样做可能会增加错误线程异常。
  84. */
  85. @SmallTest
  86. public void testBtnLayout() {
  87. final View decorView = mActivity.getWindow().getDecorView(); // 获取最上层的ViewGroup(FrameLayout)的引用
  88. ViewAsserts.assertOnScreen(decorView, mBtn); // 检测一个view是否包含在Activity的根视图View中
  89. }
  90. /**
  91. * @SmallTest 标志该测试方法是小型测试的一部分。
  92. * @MediumTest 标志该测试方法是中等测试的一部分。
  93. * @LargeTest 标志该测试方法是大型测试的一部分。
  94. * 通常情况下,如果测试方法只需要几毫秒的时间,那么它应该被标记为@SmallTest,
  95. * 长时间运行的测试(100毫秒或更多)通常被标记为@MediumTest或@LargeTest,
  96. * 这主要取决于测试访问资源在网络上或在本地系统
  97. */
  98. /**
  99. * 垃圾清理与资源回收(非必要)
  100. * @throws Exception
  101. */
  102. @Override
  103. protected void tearDown() throws Exception {
  104. super.tearDown();
  105. }
  106. }

3.跑起来试试吧

选定该文件,右键里 -》 Run “FirstAvtivityTest”

参考阅读:

http://developer.android.com/intl/zh-cn/training/activity-testing/preparing-activity-testing.html (官网文档-英)
http://hukai.me/android-training-course-in-chinese/testing/activity-testing/activity-basic-testing.html (文档中文翻译)
http://baike.baidu.com/link?url=AQZm7w1QiYYnV64-oPjmi1xS9PyPhI3WAJW8j080EawuhFGbuWbLeWKmU9MLYflGsryjsRNe8ETf1xUwGGnyXK
http://www.cnblogs.com/AloneSword/p/4109407.html
http://www.cnblogs.com/tianzhijiexian/p/4296055.html
http://yelinsen.iteye.com/blog/977736
http://www.oschina.net/question/54100_27061

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注