单元测试
从软件测试V模型,单元测试是软件测试的基础,四个方面看出单元测试的重要性:
1.时间方面—系统集成节约很多的时间
2.测试效果--单元测试是测试阶段基石,能够发现深层次的问题
3.测试成本--单元测试阶段问题容易发现
4.产品质量--单元测试好坏影响软件产品的质量.
图1:软件测试V模型
软件代码越來越多,软件单元越來越多,单元代码越來越复杂。现代编程语言是的单元测试自动化实现变得可能。XUnit框架是eXtreme Programming(XP) 的核心概念。
单元测试工具的一个重要的功能就是测试自动化,测试自动化的基础就是测试框架。最典型和最流行的单元测试是以JUnit测试框架为基础的xUnit测试框架。
XUnit根据语言不同可以分为:JUnit(Java),CppUnit(C++),DUnit(Delphi),NUnit(.Net),PhpUnit(PHP)
按类型不同可以分为:JFCUnit(GUI测试),HttpUnit(对web应用访问进行测试),JWebUnit(对HttpUnit的抽象封装),StrutsTestCase(基于JUnit的Struts应用测试框架),HtmlUnit
1xUnit单元测试框架
测试软件开发过程中必不可少的一部分,是软件质量保证的重要手段。测试对象:代码,需求文档,设计文档.代码测试:单元测试,集成测试,系统测试和验收测试。eXtremeProgramming—单元测试—xUnit测试框架。TDD以测试为开发中心,XP开发者大多使用开源的测试框架-xUnit家族。
Xunit框架的主要优点是提供了一个自动测试的解决方案。自动测试概念很重要,有了自动测试,我们就可以在开发软件项目中实施持续集成。
1.1xUnit介绍
KentBeck—Smalltalk Sunit—Erich Gamma-- Junit
框架结构表现的概念--->xUnit:一些简单的编写测试的规则--->移植到三十多种语言中(Python,C++,.NET,Html, JavaScript ,Ruby等)
xUnit是一个基于测试的单元测试框架,运行测试用例,反馈测试结果以及记录测试日志的一系列基础软件设施。TestCase,TestSuite,TestResult,xUnit.framework.Assert,TestResult,TestListener,TestRunner.
图2,xUnit 测试框架
Xunit的原则:
先编写测试代码,然后编写符合测试的代码
测试代码不需要覆盖所有的细节,但是需要覆盖主要的功能和可能出错的地方
发现Bug后,首先编写对应的测试用例,然后在进行调试
不断总结出现Bug的原因,对其他代码也编写相应的测试用例
每次编写完代码后,运行所有以前的测试用例,验证对以前的代码的影响,并把这种影响尽早消除
不断维护测试代码,保证代码变动后能通过所有测试
xUnit测试框架包括的4大要素:
测试Fixtures:是一组认定为被测对象或被测程序单元测试成功的预订条件或预期结果的设定。Fixture就是被测试的目标。
测试集(TestSuite):一组测试用例,这些测试用例要求相同的测试Fixtures,从而保证不会出现管理的上的混乱
测试执行:单元测试的执行-setUp()-->执行测试动作-->tearDown()
测试断言(Assert):实际上就是验证被测程序在测试中的行为或状态的一个宏或者函数,断言失败会引发异常,终止测试的执行。
测试用例的编写:初始化Fixture-->按照某个测试功能和流程对Fixture进行操作-->验证结果是否正确-->对Fixture相关的资源进行释放
1.2Junit单元测试工具
JUnit---SourceForge项目---IBMCommon Public License ---官网:www.junit.org
ASP,C++,c#,Effel,Delphi,Perl,PHP,Python,REBOL,Smalltalk,Visual Basic。
xUnit的目标:1.开发人员写测试代码,提供一个测试框架,减少重复性劳动2.创建并保留测试,将测试集中在一起,用已有的测试创建新的测试。
Junit的好处:
可以使测试代码与产品代码分开
针对某个类的测试代码,通过较少的改动便可以将其应用于另一个类的测试
易于集成到测试人员的构建中,JUnit和Ant的结合实施增量开发
Junit是公开源代码的,可以进行二次开发
可以方便的对JUnit进行扩展
Junit测试编写的原则:
简化测试的编写,这种简化包括测试框架的学习和实际测试单元的编写
使测试单元保持持久性
可以利用既有的测试来编写相关的测试
Junit的特征:
使用断言方法来判断期望值和实际值的差异,返回Boolean值
测试驱动设备使用共同的初始化变量或实例
测试包结构便于组织和集成运行
支持图形交互模式和文本交互模式
JUnit共有7个包,核心的包就是junit.framework和junit.runner。Framework包负责整个测试对象的架构,Runner包负责测试驱动。
JUnit有4个重要的类:TestSuite,TestCase,TestResult,TestRunner,前三个属于Framework包,后一个类在不同的环境下是不同的。后一个类在不同的环境中是不同的,文本环境和图形界面
TestResult:负责收集TestCase所执行的结果,客户可以预测的Failure和没有预测的Error。同时负责将测试结果转发给TestListener(该接口由TestRunner集成)进行处理
TestRunner:客户对象调用的起点,负责对整个测试流程进行跟踪。能够显示返回的测试结果,并报告测试的进度
TestSuite:负责包装和运行所有的TestCase
TestCase:客户测试类所要继承的类,负责测试时对客户类进行初始化以及测试方法的调用.
两个重要的接口:Test,TestListener
Test:包含两个方法:run(),countTestCases()-->用于对测试动作特征的提取
TestListener:addError(),addFailure(),startTest(),endTest()-->用于对测试结果的处理以及测试驱动开发的动作的特征的提取。
Junit框架组成:TestCase(测junit.swingui.TestRunner试用例),TestSuite(测试集),TestResult(测试结果描述与记录),TestListener(事件监听者),TestFailure(测试失败),AssertionFailedError.
Junit和Eclipse的集成的处理可以参考《JUnit in Action》
Junit的命令行处理:java junit.textui.TestRunner xxx
1.3JUnit实例
1.3.1. 开发工具:
Eclipse、JUnit、Ant
1.3.2. 编码实现三角形类型判断程序:
class Triangle{ private double a, b, c; public Triangle ( double a, double b, double c ){ this.a = a; this.b = b; this.c = c; } /* 返回值为 1 表示是等边三角形 返回值为 2 表示是等腰三角形 返回值为 3 表示是其他种类三角形 返回值为-1 表示不能构成三角形 */ public int type (){ //to be added } } |
1.3.3、基于 JUnit 设计测试用例,使得满足如下条件
(1)包含不能构成三角形的测试用例
(2)包含构成等边三角形的测试用例
(3)包含构成等腰三角形的测试用例(提示:需要三个测试用例)
(4)包含构成其他种类三角形的测试用例
(5)三角形有一条边是非正数
4、编写 ant 脚本驱动测试用例
包括测试用例执行和测试报表生成
备注:个人使用的JVM是Open JDK6.0,eclipse是3.8 Juno,JUnit使用的是4.11
判断三角形的java程序:
package demo2; public class Triangle { private double a,b,c; public Triangle(double a,double b,double c){ this.a =a; this.b =b; this.c =c; } /* * return value description: * 1.等边三角形 * 2.等腰三角形 * 3.其他三角形 * -1.不是三角形 */ public int type(){ if(isTriangle()){ if(a==b&&b==c){ return 1; }else if(a==b||b==c||c==a){ return 2; }else return 3; } else return -1; } //auxiliary method which is used to predicate weather three number consist of a triangle or not private boolean isTriangle(){ if(Math.abs(a-b)<c&&Math.abs(a-c)<b&&Math.abs(b-c)<a &&(a+b>c)&& (a+c >b)&& (b+c >a) ) return true; return false; } } |
单个的测试用例:
package demo2; import static org.junit.Assert.*; import org.junit.Test; public class TriangleTest { @Test public void testType(){ Triangle triangle = new Triangle(12,12,4); assertEquals(2,triangle.type()); } } |
参数化的测试用例:
package demo2; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(value = Parameterized.class) public class ParameterTestTriangle { private double expected; private double valueOne; private double valueTwo; private double valueThree; @Parameters //Double is a class type,double is a basic type. public static Collection<Object []> getTestParameters(){ return Arrays.asList(new Object [][]{ {2,12.3,13.5,13.5}, {2,10,10,4}, {2,34,5,34}, {1,6.0,6.0,6.0}, {1,23,23,23}, {-1,1.0,4.0,9.0}, {-1,10,3,15}, {3,3.0,4.0,5.0}, {3,12,24,33}, }); } public ParameterTestTriangle(double expected, double valueOne, double valueTwo,double valueThree){ this.expected= expected; this.valueOne= valueOne; this.valueTwo= valueTwo; this.valueThree= valueThree; } @Test public void testType(){ Triangle triangle = new Triangle(valueOne,valueTwo,valueThree); assertSame("期待类型和测试类型不同!",(int)expected,triangle.type()); } } |
结合上述的两个测试用例的测试用例集:
package demo2; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(value = Suite.class) @SuiteClasses(value={ ParameterTestTriangle.class,TriangleTest.class}) public class TestSuite { } |
编写的ant脚本如下:
<project name ="first build.xml" default ="report"> <target name="clean" > <delete dir="dist"/> <delete dir="bin/demo"/> <delete dir="report"/> </target> <target name="compile" depends="clean"> <javac srcdir="src" destdir="bin" includes="demo2/*.java" includeantruntime="true" classpath ="lib\junit.jar"/> </target> <target name="test" depends="compile"> <mkdir dir="report/html"/> <junit printsummary="yes" haltonerror="false" haltonfailure="false" fork="yes" > <formatter type="plain" usefile="false"/> <formatter type="xml"/> <test name="demo2.TestSuite" todir="report"/> <classpath> <pathelement location="bin"/> <pathelement location="lib\junit.jar"/> </classpath> </junit> <junitreport todir="report"> <fileset dir="report"> <include name="TEST*.xml"/> </fileset> <report format="frames" todir="report/html"/> </junitreport> </target> <target name="report" depends="test"> <echo>Hello world</echo> </target> </project> |
运行结果截图:
单个测试用的运行截图
参数化测试用例的运行截图:
测试用例集的运行:
ant脚本运行
1.首先cd 到相应的java project的目录下 cd java project/testSoftware
2. 输入ant 或者ant -f build.xml
3 参看结果:
Buildfile: /home/xiajian/project file/java project/testSoftware/build.xml clean: [delete] Deleting directory /home/xiajian/project file/java project/testSoftware/report compile: test: [mkdir] Created dir: /home/xiajian/project file/java project/testSoftware/report/html [junit] Running demo2.TestSuite [junit] Testsuite: demo2.TestSuite [junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.097 sec [junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.097 sec [junit] [junit] Testcase: testType[0] took 0.002 sec [junit] Testcase: testType[1] took 0 sec [junit] Testcase: testType[2] took 0 sec [junit] Testcase: testType[3] took 0 sec [junit] Testcase: testType[4] took 0 sec [junit] Testcase: testType[5] took 0 sec [junit] Testcase: testType[6] took 0 sec [junit] Testcase: testType[7] took 0 sec [junit] Testcase: testType[8] took 0.001 sec [junit] Testcase: testType took 0 sec [junitreport] Processing /home/xiajian/project file/java project/testSoftware/report/TESTS-TestSuites.xml to /tmp/null2022323935 [junitreport] Loading stylesheet jar:file:/usr/share/ant/lib/ant-junit.jar!/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-frames.xsl [junitreport] Transform time: 564ms [junitreport] Deleting: /tmp/null2022323935 report: [echo] Hello world BUILD SUCCESSFUL |
ant脚本在当前项目下生成了一个report文件夹,其中有相应的测试用例执行的报告
备注:关于这里的JUnit的实例的代码,我放到了在百度网盘上创建了一个公共链接,有兴趣的可以研究一下,这实例位于testSoftware/src/demo2的中.网盘连接:http://pan.baidu.com/share/link?shareid=1294418402&uk=556148328
在导入项目时可能出现的问题:
原先我是在OpenJDK 7的环境下编写这个测试程序的,后来从Ubuntu12.10换到Ubuntu12.04LTS后,系统安装的JDK 是Open JDK 6,所以导入项目时出现了java.lang.UnsupportedClassVersionError: demo2/ParameterTestTriangle : Unsupported major.minor version 错误,百度了一下找到了问题的原因:是因为使用高版本的JDK编译的Java class文件试图在较低版本的JVM上运行. 解决方法:
一定要清理干净已经生成的文件
替换成正确的JDK--修JDK的jre以及相的编译器(修改的方法是右击项目属性build path和build compiler中修改)
修改JRE:
修改编译器:
参考资料:
[1]软件测试实验指导教程/蔡建平, 清华大学出版社, 2009.11
[2] JUnit实战(第二版)/Peter Tahchiev等著,王魁译, 人民邮电出版社,2012.4
[3]软件测试v模型的图片来源:http://leochael.blog.163.com/blog/static/1309571200741181733167/
[4]出现的java错误:http://zhidao.baidu.com/question/455404569.html
相关文章:
软件测试实验学习笔记系列2 -- lint,splint的使用
软件测试实验学习笔记系列4--CppUnit