实验五 单元测试
一、实验目的
1)掌握单元测试的方法
2) 学习XUnit测试原理及框架;
3)掌握使用测试框架进行单元测试的方法和过程。
二、实验内容与要求
1、了解单元测试的原理与框架
1.1 单元测试原理
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。
单元测试的内容包括
模块接口测试、局部数据结构测试、路径测试、错误处理测试、边界测试
。在源程序代码编制完成,经过评审和验证,确认没有语法错误之后,就开始进行单元测试的测试用例设计。利用设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。对于每一组输入,应有预期的正确结果。
1.2 测试框架
xUnit是各种代码驱动测试框架的统称,这些框架可以测试 软件的不同内容(单元),比如函数和类。xUnit框架的主要优点是,它提供了一个自动化测试的解决方案。可以避免多次编写重复的测试代码。
底层是xUnit的framwork,xUnit的类库,提供了对外的功能方法、工具类、api等
TestCase(具体的测试用例)去使用framwork
TestCase执行后会有TestResult
使用TestSuite控制TestCase的组合
TestRunner执行器,负责执行case
TestListener过程监听,监听case成功失败以及数据结果,输出到结果报告中
Unit测试框架包括四个要素:
(1)测试目标(对象)
一组认定被测对象或被测程序单元测试成功的预定条件或预期结果的设定。Fixture就是被测试的目标,可以是一个函数、一组对象或一个对象。 测试人员在测试前应了解被测试的对象的功能或行为。
(2)测试集
测试集是一组测试用例,这些测试用例要求有相同的测试Fixture,以保证这些测试不会出现管理上的混乱。
(3)测试执行
单个单元测试的执行可以按下面的方式进行:
第一步 编写 setUp() 函数,目的是:建立针对被测试单元的独立测试环境;举个例子,这可能包含创建临时或代理的数据库、目录,再或者启动一个服务器进程。
第二步 编写所有测试用例的测试体或者测试程序;
第三步 编写tearDown()函数,目的是:无论测试成功还是失败,都将环境进行清理,以免影响后续的测试;
(4)断言
断言实际上就是验证被测程序在测试中的行为或状态的一个函数或者宏。断言的失败会引发异常,终止测试的执行。
(一)源码https://github.com/bigApple00/four-types-compute/tree/master/ind/calc
1 package ind.calc; 2 import java.util.Random; 3 import java.util.regex.Matcher; 4 import java.util.regex.Pattern; 5 6 public class Logic { 7 8 static Random random =new Random(); 9 10 public static int produceNumber1() { //产生被除数 11 int random_number_x; 12 random_number_x=random.nextInt(50)+1; 13 return random_number_x; 14 } 15 16 public static float produceNumber2() { //产生除数 17 int random_number_y; 18 random_number_y=random.nextInt(50)+1; 19 return random_number_y; 20 } 21 22 public static void underHundred() { //使加法结果小于一百 23 while(UI.x+UI.y>100) { 24 UI.y--; 25 if(UI.x+UI.y<100) 26 break; 27 } 28 } 29 30 public static void underHundred2() { //使乘积结果小于一百 31 while(UI.x*UI.y>100) { 32 UI.y--; 33 if(UI.x*UI.y<100) 34 break; 35 } 36 } 37 38 39 public static void bigSubtractsmall() { //完成大减小 40 while(UI.x-UI.y<0) { 41 UI.x++; 42 if(UI.x-UI.y>0) 43 break; 44 } 45 } 46 47 public static void exactDivide() { //使整除 48 try { 49 UI.z=UI.x/UI.y; 50 } 51 catch (ArithmeticException e) { 52 e.printStackTrace(); 53 } 54 55 Pattern pattern=Pattern.compile("\\.0$"); //若不是以.0结尾,就没有整除 56 String s = String.valueOf(UI.z); 57 Matcher matcher=pattern.matcher(s); 58 while(!matcher.find()) { //改变被除数y的值 59 UI.y=1f; 60 UI.y++; 61 UI.z=UI.x/UI.y; 62 s = String.valueOf(UI.z); 63 matcher=pattern.matcher(s); 64 if(matcher.find()) 65 break; 66 } 67 } 68 69 public static String chooseCharacter() { //随机选择四种运算符的一个 70 char[] a={'+','-','*','/'}; 71 String charString=String.valueOf(a[random.nextInt(a.length)]); 72 return charString; 73 74 } 75 76 }
(二)Eclipse 中JUnit导入过程
Eclipse 集成了 JUnit,可以非常方便地编写 Test Case。Eclipse 自带了一个 JUnit 插件,不用安装就可以在项目中测试相关的类,并且可以调试测试用例和被测类。建立一个基于 JUnit4 的测试项目,对一个类当中的多个方法进行单元测试。
在同一个项目下新建source folder文件,新建同样的包名,新建类JunitTest。
对自己的项目选择属性,将 JUnit4 单元测试包引入这个项目:在该项目上点右键,点“属性”,在弹出的属性窗口中,首先在左边选择“Java Build Path”,然后到右上选择“Libraries” 标签,之后在最右边点击“Add Library…”按钮,如下图所示:然后在新弹出的对话框中选择 JUnit4 并点击确定,JUnit4 软件包就被包含进我们这个项目了。
生成 JUnit 测试框架:在 Eclipse 的 Package Explorer 中用右键点击该类弹出菜单,选择“JUnit 测试用例”。在弹出的对话框中,进行相应的选择,如下图所示:在class under test中选择自己测试的类。
点击“下一步”后,系统会自动列出你这个类中包含的方法,选择你要进行测试的方法。
指定某个测试方法运行之前运行某个初始化方法,这个方法的名称必须是setUp,如果希望在某个测试方法运行之后运行某个释放资源的方法,这个方法的名称必须是tearDown。
(三)测试代码加结果分析
我们对其中的四个方法进行测试,本实验中为静态方法,setUp方法中不要初始化方法。
(ChooseCharacter为返回+,-,*,/方法,静态审查即可,无需测试)
(1)对第一个类Logic测试
1.对加法乘和法小于一百方法进行测试
测试所用方法为 函数原型:assertTrue ([String message],Boolean condition) message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 condition是待验证的布尔型值。该断言用来验证给定的布尔型值是否为真,假如结果为假,则验证失败。
由于变量定义错误,在执行方法时导致参数为空,测试不出来,测试代码如下图。
下图为被测代码
改为下图,引入Ui类中的变量。第一个加法小于一百的方法和第二个乘法小于一百的方法测试通过。
2.测试大减小方法
代码如下,但是测试不通过。原因:算法有问题。
改为如下代码测试通过
3.测试整除方法
源代码用正则表达式,很麻烦,测试不通过。源码如下图
改为下图代码测试通过
(2)对Ui类测试
1.构造方法Ui
在测试类中声明Ui对象,在setUp方法中创建对象g,在testUi中测试,调用断言方法 函数原型:assertNotNull([String message],Object object) 若对象不为空则验证正确。
2.actionPerformed和main方法通过运行来测试
测试结果:填入数据之后,点击按钮可以正确显示出答题正确的个数。测试通过。
https://github.com/bigApple00/four-types-compute
思考题:
比较以下二个工匠的做法,你认为哪种好?结合编码和单元测试,谈谈你的认识。
第一个好,日常生活中,工匠都是采用方法一,有了参考才不会砌歪,工匠二一旦砌歪要改动很多的砖。编码中也是如此,单元测试就像水平线,模块就像砖,项目就是墙,有了单元测试检测,成本才会降低,项目周期更短,维护更容易。
小结:
单元测试真的很有用,本来看到实验内容那么多,都不太想做。做了之后发现确实很有用,可以发现问题,就比如我的实现两个数整除部分的方法,最开始是使用正则表达式来判断,导致程序运行的很慢,数据也很局限。测试之后进行修改算法,运行的更快了,数据也变得更广,更合理。actionPerformed方法应该通过运行来测试,没想到合适的测试用例。生产实习整了那么多Java代码,程序员真的要学好多东西啊。
完