1. 单元测试的作用:
许多开发者都有个习惯,常常不乐意去写个简单的单元测试程序来验证自己的代码。对自己的程序一直非常有自信,或存在侥幸心理每次运行通过后就直接扔给测试组测试了。然而每次测试组的BUG提交过来后就会发现自己的程序还存在许多没有想到的漏洞。但是每次修改好BUG以后还是怀着侥幸心理,认为这次不会有bug了。然后又一次自信地提交,结果又败了。其实在项目开发的过程中,实际编程的时间并不是特别多,而真正耗时的往往是调试和修改阶段。因此单元测试的覆盖是很有必要的。
一般认为,单元测试有四种作用:
(1)使代码可以放心修改和重构;
(2)迫使程序员从调用者而不是实现者的角度设计软件模块;
(3)迫使程序员将软件模块写得易于测试和调用,从而有利于解耦;
(4)测试本身可作为被测代码的用法说明,从而替代了一部分文档功能。
2. JUnit单元测试框架:
2.1. 简介:
JUnit 是一个开源测试框架,用于编写和运行可重复的自动化测试,让我们可以确保我们的代码按预期工作。 JUnit 广泛应用于行业,可以用作单独的Java程序(在命令行)或在一个IDE如Eclipse内。 JUnit提供:
- 断言测试预期结果.
- 测试功能共享通用的测试数据.
- 测试套件轻松地组织和运行测试.
- 图形和文本测试运行.
2.2. 简单使用:
2.2.1 下载安装
目前为止最新的版本是JUnit4,很多IDE都会集成进去。如果没有可以下载这两个包导入
2.2.2. JUnit的注释
2.2.3. JUnit的断言
2.2.4. 简单示例
Caculate.java:
public class Caculate { int add(int numberA, int numberB){ return numberA + numberB; } }
CaculateTest.java:
import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; public class CaculateTest { private Caculate caculate; @Before public void init(){ if(caculate == null){ caculate = new Caculate(); } } @Test public void testAdd(){ int result = caculate.add(5, 3); assertEquals(8, result); } }
右键点击CaculateTest.java的Run as --> JUnit Test:
3. Android中的单元测试
3.1. Android单元测试框架
Android基于JUnit专门写了一套测试框架(Instrumented Test), 用于支持其单元测试和验收测试. 类继承关系:
3.2. 简单示例
用这套框架可以不使用JUnit的注解,直接在要测试的方法前面带上test前缀就可以。我们按需要继承这些基类就可以编写自己所需要的单元测试。Android studio的项目会自动生成一个src/androidTest/java文件夹,单元测试类就放在这个文件夹下面,目录结构如下:
在src/main/java/com/junittest包下新建一个MainActivity.java, 里面有一个按钮和一个文本,点击按钮后可以改变文本内容。
public class MainActivity extends Activity { private TextView mTextView; private Button mSubmitButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mTextView = (TextView) findViewById(R.id.hello_text); mSubmitButton = (Button) findViewById(R.id.submit_button); mSubmitButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mTextView.setText("Change from Click!"); } }); } public String getTextString(){ if(mTextView != null){ return mTextView.getText().toString(); } return ""; } }
在src/androidTest/javacom/junittest包下新建一个MainActivityTest.java, MainActivityTest继承InstrumentationTestCase:
package com.junittest; import android.content.Intent; import android.test.InstrumentationTestCase; import android.util.Log; import android.widget.Button; import android.widget.TextView; /** * Created by caihanyuan on 15-10-28. */ public class MainActivityTest extends InstrumentationTestCase { final static String TAG = MainActivityTest.class.getName(); private MainActivity mActivity; private TextView mTextView; private Button mButton; /** * 单元测试开始之前处理一些初始化工作 * @throws Exception */ @Override protected void setUp() throws Exception { Intent intent = new Intent(); intent.setClassName("com.junittest", MainActivity.class.getName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mActivity = (MainActivity) getInstrumentation().startActivitySync(intent); mTextView = (TextView) mActivity.findViewById(R.id.hello_text); mButton = (Button) mActivity.findViewById(R.id.submit_button); } /** * 单元测试结束后处理一些资源释放工作 * @throws Exception */ @Override protected void tearDown() throws Exception { mActivity.finish(); super.tearDown(); } public void testInit() { assertNotNull(mActivity); assertNotNull(mTextView); assertNotNull(mButton); } public void testGetText() { String textString = mActivity.getTextString(); assertEquals("Hello World!", textString); } public void testChangeText() { assertNotNull(mTextView); getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { mTextView.setText("ATest"); assertEquals("ATest", mActivity.getTextString()); } }); } public void testClickButton(){ assertNotNull(mButton); getInstrumentation().runOnMainSync(new Runnable(){ @Override public void run() { mButton.performClick(); assertEquals("Change from Click!", mActivity.getTextString()); } }); } public void testGetActivity() { Log.d(TAG, mActivity.getPackageName()); } }
测试结果:
由于篇幅有限,这里只是做简单的介绍,Android测试框架的各个基类的用途可以参考官网。
3.3. 单元测试框架Robolectric
Robolectric is a framework that allows you to write unit tests and run them on a desktop JVM while still using Android API. Robolectric mocks part of the Android framework contained in the android.jar
file. This is similar to the unit test support via the Android Gradle plug-in but Robolectric provides also implementations for the methods while the Android unit testing support throws exceptions in all in android.jar methods which are used for unit testing.
因此Robolectric的设计目的是让我们能在本地JVM上直接进行Android的单元测试,这样就不用等待Android真机或模拟器中打包,安装,运行的过程。大大提高了单元测试的效率。
Robolectric的介绍和使用篇幅比较长,这里就不写出来了。参考文献中附有相关的资料,感兴趣的同学可以看看哦。
参考文献:
单元测试的好处:http://www.blogjava.net/square/articles/158103.html
Junit官网:http://junit.org/
Android单元测试: http://developer.android.com/intl/zh-cn/training/activity-testing/index.html
Robolectric官网:http://robolectric.org/