2.3 应用JUnit对Android项目进行单元测试
2.3.1 JUnit基于Android项目TestCase的应用
如果我们对基于Android系统项目开发有一定了解的话,相信一定能看出来,其核心代码是计算两个整数相加的函数。我们做单元测试当然也挑选其最核心的函数来进行测试。可以按照如下的步骤来创建一个基于Android项目的测试用例(TestCase)。
第一步:选中“CalculatorOfTwoNum”项目下的“src”目录中的“com.yuy.calculatoroftwonum”包里的“MainActivity.java”文件,单击鼠标右键,从弹出的快捷菜单中选择“New”菜单项,在其弹出的子菜单项中,再选择“JUnit Test Case”选项,如图2-3所示。
在弹出的图2-4所示对话框中,我们选择“New JUnit 4 test”选项。为了便于我们对测试相关的用例进行管理,同时也为了不和原始的项目源代码混在一块,我们将测试用例放到“com.yuy.calculatoroftwonum.test”包下,因为是针对“MainActivity.java”文件进行的用例设计,所以在Name后的文本框输入“MainActivityTest”,该类的父类我们选择“android.test.AndroidTestCase”,创建“setUp()”和“tearDown()”方法。单击“Next”按钮,出现图2-5所示对话框,这里我们主要针对其关键的“add(int,int)”函数来进行测试,因此选中其前面的复选框,单击“Finish”按钮。
我们在“src”目录下,发现新创建了一个“com.yuy.calculatoroftwonum.test”包,同时Eclipse自动帮我们打开了新建的“MainActivityTest.java”文件,如图2-6所示。
为了能够让大家更清楚的看到“MainActivityTest.java”文件内容,我们将其源码贴出来,如下所示。
package com.yuy.calculatoroftwonum.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import android.test.AndroidTestCase;
public class MainActivityTest extends AndroidTestCase {
@Before
protected void setUp() throws Exception {
super.setUp();
}
@After
protected void tearDown() throws Exception {
super.tearDown();
}
@Test
public void testAdd() {
fail("Not yet implemented");
}
}
在生成的测试用例源文件中看到Eclipse自动帮我们创建好了3个函数,即setUp()、tearDown()和testAdd()函数。那么这些函数都是用来做什么的呢?通常setUp()函数用来完成一些初始化的工作,比如创建被测试应用实例或者我们在测试应用的业务时可能会需要登录系统,那么可以将登录放在该部分;tearDown()函数则主要完成一些收尾性的工作,比如释放对象、资源等或者系统的登出;而testAdd()函数就是我们要测试的一些方法、函数,也就是图2-6所示我们选择的测试方法,通常系统自动的帮我们在被测试的方法、函数的前面加上了一个“test”前缀,这是因为在JUnit3里,测试类必须继承TestCase类,方法必须是以“test”开头;在JUnit4里面,采用Annotation的JUnit已经不会霸道的要求必须继承自TestCase了,而且测试方法也不必以“test”开头了,只要以@Test注解来描述即可,无需继承TestCase类。JUnit 设计的非常小巧,但是功能却非常强大。Martin Fowler如此评价JUnit:在软件开发领域,从来就没有如此少的代码起到了如此重要的作用。它大大简化了开发人员执行单元测试的难度,特别是JUnit 4使用Java 5中的注解(Annotation)使测试变得更加简单。为了让大家对JUnit 4的注解有一个认识,这里我简单向大家介绍一下。
JUnit 4使用Java 5中的注解(Annotation),以下是JUnit 4常用的几个Annotation介绍。
@Before:初始化方法;
@After:释放资源;
@Test:测试方法,在这里可以设计一些测试用例,正常的、异常的测试用例;
@Ignore:忽略的测试方法;
@BeforeClass:针对所有测试,只执行一次,且必须为static void;
@AfterClass:针对所有测试,只执行一次,且必须为static void。
我们针对add()函数,想设计3个正常情况下的测试用例,如表2-1所示。当然还可以根据需要设计一些异常情况下的测试用例,因这里只是对JUnit框架的介绍,所以不予过多赘述,如果读者朋友们对此感兴趣,建议看系统的JUnit方面的书籍。
为此,我们编写的单元测试用例(MainActivityTest.java)源代码如下。
package com.yuy.calculatoroftwonum.test;
import com.yuy.calculatoroftwonum.MainActivity;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import android.test.AndroidTestCase;
public class MainActivityTest extends AndroidTestCase {
MainActivity myapp = null;
@Before
protected void setUp() throws Exception {
super.setUp();
myapp = new MainActivity();
}
@After
protected void tearDown() throws Exception {
super.tearDown();
myapp = null;
}
@Test
public void testAdd() {
assertEquals(myapp.add(3,2),5);
assertEquals(myapp.add(1,99),100);
assertEquals(myapp.add(1,10000),10001);
}
}
从上面的测试用例源代码中,我们能清楚的看到引入了JUnit框架的断言语句。什么叫断言呢?JUnit为我们提供了一些辅助函数,用来帮助我们确定被测试的方法是否按照预期的效果正常工作,通常把这些辅助函数称为断言。像在本测试用例源代码中使用的“assertEquals()”函数,myapp.add(3,2)是其第一个参数,它返回的是一个数字,其值应该为5,与第二个参数5是相同的,这样因为它们完全匹配,所以该断言函数的返回值为真。而后面的2个用例的断言应该也为真,关于JUnit的断言还有很多,如assertTrue()、assertNull()、assertSame()等函数,通常它们有一个明显的标记就是函数名称前面都带有“assert”。
接下来,我们要查看JUnit 4的相关库文件是否被配置,单击该项目的任意文件或者是包,而后单击鼠标右键,从快捷菜单中选择“Build Path”>“Configure Build Path …”菜单项,如图2-7所示。
请选择“Libraries”页,查看JUnit 4是否被添加,如果没有请单击“Add Library…”进行添加,如图2-8所示。
还要在“Order and Export”页查看“JUnit 4”是否被选中,如果没有被选中,则需选中该项,如图2-9所示。
而后,还需要在“AndroidManifest.xml”进行如下配置,“AndroidManifest.xml”文件的具体内容如下。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.yuy.calculatoroftwonum"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
< uses-library android:name="android.test.runner" />
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.yuy.calculatoroftwonum" android:label="Tests for My App" />
</manifest>
粗、黑字体部分内容为要添加的内容,即在“”增加引用“android.test.runner”的声明,如下所示。
,同时还需要在“”中增加“instrumentation”的信息说明,如下所示。
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.yuy.calculatoroftwonum" android:label="Tests for My App" />。
上述过程如果配置正确的话,选中“MainActivityTest.java”,单击鼠标右键,选择“Run As”>“Android JUnit Test”,如图2-10所示。
这里我们应用我的手机作为测试设备,在弹出的图2-11设备选择列表,选中我的三星手机,而后单击“OK”按钮。
我们会发现在我的手机上多了一个被测试的手机应用,如图2-12所示。
同时,还发现Eclipse自动会调出“JUnit”视图,如图2-13所示。
我们可以看到“testAdd”和“testAndroidTestCaseSetupProperly”全部执行成功,上面的进度条全为绿颜色(表示实际的结果和预期的结果一致,也就是断言为真),如果出错则显示为红颜色(表示实际的结果和预期的结果一致,也就是断言为假)。这里我们发现多出来一个“testAndroidTestCaseSetupProperly”,它是怎么来的呢?可以双击它的名称,则出现如图2-14所示信息。
我们可以看到“testAndroidTestCaseSetupProperly”隶属于“AndroidTestCase.class”类,它主要是判断是否存在上下文,因为不为空所以其为真。而双击“testAdd”则显示其相关内容,如图2-15所示。
2.3.2 JUnit基于Android项目TestSuite的应用
无论我们从事的是手工测试还是自动化测试,有的时候可能会碰到一些时间紧、任务重的情况,有的时候可能在短短的二、三十分钟后就需要将版本部署到互联网环境,无论哪种方式对我们来讲可能都是来不及完成的,此时我们就必须要评估风险,挑一些基础的、重要的业务以及与修改的缺陷相关的内容或者是优先级高的测试用例来执行。那么作为自动化测试工程师的我们又该怎样去实施呢?作为自动化测试工程师,平时在日常的工作中就要养成对自动化测试用例进行分级管理,同时不仅仅是我们要有自动化测试用例,还要有一套框架来管理测试用例按照不同的测试优先级、硬件资源利用率、甚至是结合不同的应急性测试情况手工选择要测试的内容等多种方式来执行这些自动化测试用例,并能够汇集执行结果,分发测试报告等。当然每个单位测试人员的能力不同、单位的实际情况不同、单位对测试人员的要求不同、单位对自身产品的质量要求也不同,大家要因地制宜,满足单位、研发部门、测试部门对产品的定位、产品质量的定位以及发展战略的一些要求等。言归正传,我们接着讲实施自动化测试另一个很重要的内容,测试集(Test Suite)。测试集(TestSuite)是一系列测试用例(testcase)的集合,我们可以根据需要将不同优先级、不同考察功能内容要运行的测试用例添加到测试集当中,方便测试用例的管理、执行。
下面就让我们一起来了解一下如何应用测试集。启动Eclipse,打开“CalculatorOfTwoNum”项目,然后,选中“src”,单击鼠标右键,从弹出的快捷菜单中选择“New”>“Class”,如图2-16所示。
如图2-17所示,我们将该测试集命名为“TestSuiteSample”,仍然放入“com.yuy.calculatoro- ftwonum.test”下,其父类为“junit.framework.TestSuite”,而后单击“Finish”按钮。
如图 2-18 所示为了让大家了解如何在测试集中执行多个测试用例以及如何仅执行测试用例文件中的特定的测试用例,我们再添加一个测试用例的源文件(MainActivityTest1.java),其代码如下。
package com.yuy.calculatoroftwonum.test;
import com.yuy.calculatoroftwonum.MainActivity;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import android.test.AndroidTestCase;
public class MainActivityTest1 extends AndroidTestCase {
MainActivity myapp = null;
@Before
protected void setUp() throws Exception {
super.setUp();
myapp = new MainActivity();
}
@After
protected void tearDown() throws Exception {
super.tearDown();
myapp = null;
}
@Test
public void Add测试用例1() {
assertEquals(myapp.add(3,2),5);
}
public void Addtest2() {
assertEquals(myapp.add(3,22),26);
}
public void Addtest() {
assertEquals(myapp.add(3,2),5);
}
}
从上面的代码我们可以看到,我们添加了3个测试用例,即Addtest()、Add测试用例1()和Addtest2(),也许大家已经发现了2个问题,第一个问题是我们发现在用例函数的名称中包含了中文,这是可以的,如果有此需要可以这样写。第二个问题是,我们故意写错了一个断言就是Addtest2()中的“assertEquals(myapp.add(3,22),26);”本来正确的预期输出应该为“25”,这里我们却故意的写错了,期望值写成了“26”。
这里我们修改“TestSuiteSample.java”文件内容,最终其源代码如下所示。
package com.yuy.calculatoroftwonum.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class TestSuiteSample extends TestSuite {
public static Test suite() {
TestSuite testasuite = new TestSuite("test");
testasuite.addTestSuite(MainActivityTest.class);
testasuite.addTest(TestSuite.createTest(MainActivityTest1.class,"Add测试用例1"));
testasuite.addTest(TestSuite.createTest(MainActivityTest1.class,"Addtest2"));
testasuite.addTest(TestSuite.createTest(MainActivityTest1.class,"Addtest"));
return testasuite;
}
}
从上面的源代码,我们不难发现" TestSuiteSample"类是从"TestSuite"中继承下来的,其定义了一个名为"suite"的静态函数,这是JUnit要求的做法,JUnit通过这种方式才能发现测试集的实际定义,接下来定义了这个测试集的名称为“test”,而TestSuite类提供了2个方法,即addTestSuite()和addTest()方法,“testasuite.addTestSuite(MainActivityTest.class);”就是“MainActivityTest”中的所有测试用例都添加到我们定义的“test”测试集中,而应用addTest()方法就可以添加特定的测试用例,比如这里我们应用“testasuite.addTest(TestSuite.createTest (MainActivityTest1.class,"Add测试用例1"));”,就将“Add测试用例1”测试用例,添加到“test”测试集中,后续用类似的方法将“Addtest2”和“Addtest”都加入到“test”测试集中。
接下来,我们选择“TestSuiteSample.java”文件,单击鼠标右键,选择“Run As”>“Android JUnit Test”菜单项,如图2-19所示。
这里我们仍然应用我的手机作为测试设备,在弹出的图2-20设备选择列表,选中我的三星手机,而后单击“OK”按钮。
我们会发现在我的手机上多了一个被测试的手机应用,如图2-21所示。
同时,还会发现Eclipse自动会调出“JUnit”视图,如图2-22所示。
我们可以看到运行显示为红色,说明有的测试用例执行是失败的,再看上方的“Runs:5/5 Errors:0 Failures:1”,表示运行了5个测试用例,失败1个,所以就为红颜色了。再往下看,下方是我的设备相关信息,在其下对应的是2个以完整的测试类命名,其下包含对应执行的该类测试用例名称,同时可以发现它们的左侧都有一个对应的状态图标,即:图片 86和图片 87。我们可以看到“com.yuy.calculatoroftwonum.MainActivityTest”下的“testAdd”和“testAndroidTestCaseSetupProperly”全部执行成功,而“com.yuy.calculatoroftwonum. MainActivityTest1”下的“Add测试用例1”和“Addtest”是执行成功的,而“Addtest2”是执行失败的,我们可以双击Addtest2,则在JUnit下方输出具体的错误信息,如图2-23所示。