Android单元测试
来源:互联网 发布:淘宝客服名字大全 编辑:程序博客网 时间:2024/05/29 16:33
什么是单元测试?(摘自百度百科)
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
单元测试的好处?
- 大幅度提高后期测试和维护成本。
- 可以写出高质量的代码,而且还能提高编程水平。
- 最重要的是思维上和意识上的提升。
针对Android:
每次修改代码后需要重新编译,对于复杂的App,可能花费几分钟。极大浪费了时间,因此为了减少bug,提高代码质量,单元测试是十分被需要的。
单元测试要点
- 单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能。单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的。
- 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复、改进或重构之后的正确性。
1. 接口功能测试
- 用来保证接口功能的正确性。
2. 局部数据结构测试(不常用)
- 用来保证接口中的数据结构是正确的
- 比如变量有无初始值
3. 边界条件测试
- 变量是否溢出
- 变量没有赋值(即为NULL)
- 变量是数值(或字符)
- 主要边界:最小值,最大值,无穷大(对于DOUBLE等)
- 溢出边界(期望异常或拒绝服务):最小值-1,最大值+1
- 临近边界:最小值+1,最大值-1
一些例子
- 空字符串
- 空集合
- 调整次序:升序、降序
- 变量有规律,比如对于Math.sqrt,给出n^2-1,和n^2+1的边界
4. 独立执行通路测试
- 保证每一条代码,每个分支都经过测试
代码覆盖率
- 语句覆盖:保证每一个语句都执行到了
- 判定覆盖(分支覆盖):保证每一个分支都执行到
- 条件覆盖:保证每一个条件都覆盖到true和false(即if、while中的条件语句)
- 路径覆盖:保证每一个路径都覆盖到
Android中的单元测试
举例,来测试下面的Activity,在EditText中输入字符,然后点击按钮,在TextView显示输入的字符。
public class FirstActivity extends Activity { private static final String TAG = "FirstActivity"; @Bind(R.id.first_et) EditText mEt; @Bind(R.id.first_tv) TextView mTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); ButterKnife.bind(this); Log.d(TAG,"onCreate First"); } @OnClick(R.id.first_btn) public void setTextToView() { mTv.setText(mEt.getText().toString()); } //....}
1.搭建测试环境
AndroidManifest.xml中添加权限与配置
在Application外加入
<uses-permission android:name="android.permission.RUN_INSTRUMENTATION" /> <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.zero.androidtranningdemo" />
在Application内加入
<uses-library android:name="android.test.runner" />
之后所使用的测试库是自带的。
2. 编写测试用例(androidTest里)
建议与上面文件夹一一对应
package com.zero.androidtranningdemo.activities;import android.app.Instrumentation;import android.test.ActivityInstrumentationTestCase2;import android.test.TouchUtils;import android.test.UiThreadTest;import android.test.ViewAsserts;import android.test.suitebuilder.annotation.MediumTest;import android.test.suitebuilder.annotation.SmallTest;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.zero.androidtranningdemo.R;/** * Created by zero on 15-9-21. */public class FirstActivityTest extends ActivityInstrumentationTestCase2<FirstActivity> { // 构造函数是由测试用的Runner调用,用于初始化测试类的。 public FirstActivityTest() { super(FirstActivity.class); } private Instrumentation mInstrumentation; // 我们可以将它理解为一种没有图形界面的,具有启动能力的,用于监控其他类(用Target Package声明)的工具类 private FirstActivity mActivity; private TextView mTv; private EditText mEt; private Button mBtn; /** * setUp()方法是由测试Runner在其他测试方法开始前运行的。(必要) * 1.调用父类构造函数,这是JUnit要求的。 * 2.初始化测试数据集的状态,具体而言: * a.定义保存测试数据及状态的实例变量 * b.创建并保存正在测试的Activity的引用实例。 * c.获得想要测试的Activity中任何UI组件的引用。 * * @throws Exception */ @Override protected void setUp() throws Exception { super.setUp(); // 把touch mode设置为真可以防止在执行编写的测试方法时,人为的UI操作获取到控件的焦点 // 确保在调用getActivity()方法前调用了setActivityInitialTouchMode // setActivityInitialTouchMode(true); mInstrumentation = getInstrumentation(); mActivity = getActivity(); mTv = (TextView) mActivity.findViewById(R.id.first_tv); mEt = (EditText) mActivity.findViewById(R.id.first_et); mBtn = (Button) mActivity.findViewById(R.id.first_btn); } /** * 测试前提,检查测试数据集的设置是否正确,以及我们想要测试的对象是否已经正确地初始化。 */ public void testPreconditions() { assertNotNull("mActivity is null", mActivity); assertNotNull("mTv is null", mTv); assertNotNull("mEt is null", mEt); assertNotNull("mBtn is null", mBtn); } /** * 编写的测试方法 */ // @UiThreadTest 可用于子线程处理 @MediumTest public void testClickBtn() { final String str = "Hello"; // 同步处理 mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { mEt.setText(str); // 设置测试数据 mBtn.requestFocus(); // 得到焦点 mBtn.performClick(); // 模拟点击事件 assertEquals(str, mTv.getText().toString()); // 判断结果 } });// 如果不是Btn,其它View的点击可使用TouchUtils// TouchUtils.clickView(this, mBtn); } /** * 注意:TouchUtils辅助类提供与应用程序交互的方法可以方便进行模拟触摸操作。 * 我们可以使用这些方法来模拟点击,轻敲,或应用程序屏幕拖动View。 * <p/> * 警告:TouchUtils方法的目的是将事件安全地从测试线程发送到UI线程。 * 我们不可以直接在UI线程或任何标注@UIThread的测试方法中使用TouchUtils这样做可能会增加错误线程异常。 */ @SmallTest public void testBtnLayout() { final View decorView = mActivity.getWindow().getDecorView(); // 获取最上层的ViewGroup(FrameLayout)的引用 ViewAsserts.assertOnScreen(decorView, mBtn); // 检测一个view是否包含在Activity的根视图View中 } /** * @SmallTest 标志该测试方法是小型测试的一部分。 * @MediumTest 标志该测试方法是中等测试的一部分。 * @LargeTest 标志该测试方法是大型测试的一部分。 * 通常情况下,如果测试方法只需要几毫秒的时间,那么它应该被标记为@SmallTest, * 长时间运行的测试(100毫秒或更多)通常被标记为@MediumTest或@LargeTest, * 这主要取决于测试访问资源在网络上或在本地系统 */ /** * 垃圾清理与资源回收(非必要) * @throws Exception */ @Override protected void tearDown() throws Exception { super.tearDown(); }}
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
- android 单元测试
- Android单元测试
- Android单元测试
- android单元测试
- Android单元测试
- Android单元测试
- android单元测试
- android单元测试
- Android单元测试
- Android单元测试
- android 单元测试
- android 单元测试
- Android--单元测试
- android单元测试
- Android单元测试
- Android单元测试
- Android单元测试
- ANDROID 单元测试
- 黑马程序员_JAVA获取网页源代码
- [leetcode64]Minimum Path Sum
- Android Log等级的介绍
- android sqlite3命令查询及修改数据
- OC基础-OC三大基本数据结构-NSString
- Android单元测试
- 判断是否是闰年
- Socket网络编程(一)
- 深入浅出UML类图(三)
- Apache2 httpd.conf 配置详解
- jsp使用注释格式问题
- 新建JSP报错:The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
- HashMap多线程并发问题分析
- 域名到ip地址转换