使用 JUnit 测试 Java 应用程序 课堂笔记(三)

一.测试驱动的开发(TDD)

1.定义

  • 先开发测试用例,再编写应用程序代码,用于驱动软件设计。

2.步骤:

  • ①创建测试场景 编写测试用例
  • ②编写应用程序代码
  • ③运行测试 若失败则重复②③ / 若成功则④ 
  • ④重构应用程序代码

3.TDD 优点

  • 调试时间减少:代码根据定义的测试用例开发。
  • 灵活、可维护和可扩展的代码:采用此技术开发的软件是松散耦合且可轻松维护的。
  • 关键分析和设计:设计了测试场景并编写了测试用例时才开发代码。
  • 可执行文档:测试用例可由其他程序员参考,以了解代码用法及其用途。
  • 面向用户的开发:测试用 例是从用户角度编写的。

4.有效测试的准则

  • 识别并测试方法的 边界条件,因为方法实际上是在其边界值上生成意外结果。
  • 验证 测试结果,因为还要预料得到不正确的结果。
  • 了解意外问题,借助模拟对象 模拟这些错误 并将它们注入代码。
  • 通过不同策略 交叉检查 测试用例的结果。
  • 应用程序代码应该进行良好 优化 以处理大型复杂值,并且必须始终返回准确结果。

5.有效测试的属性

  • 自动: 单元测试应该 自动运行,并且没有任何外部依赖,所有连接和配置应该由测试完成。
  • 彻底: 测试用例应该覆盖代码的所有方面并提供 100% 的代码覆盖率。
  • 独立测试用例应该是 独立 于其他测试用例并且松散耦合的。不得对其他测试用例或者类具有任何依赖。
  • 专业测试应该符合生产 标准 并且代码应该发送给生产服务器。
  • 可重复测试用例应该是 可重复的,并且在执行时生成同样结果。

6.常见编程问题

  • 不可忽视中断失败的测试用例
  • 如果代码处理小数值,那么应该总是使用浮点数对它进行测试
  • 避免测试用例长时间运行
  • 创建低耦合的测试用例
  • 测试应该总是生成同样结果,无论在什么机器上执行
  • 不要将任何类型的生产代码或者配置代码放到 main() 方法中

二.连续集成测试(CI)及使用 Jenkins

1.基本概念

CI 测试:应用程序将以一定的时间间隔彼此集成,以验证整体功能和稳定性。

CI 测试优点:

  • 提供便捷的单元集成 
  • 允许自动部署
  • 提供项目的运行状况报告
  • 实现独立于平台的开发
  • 提供项目的更好可视性

实现 CI 连续集成测试的工具(独立平台):

  • Hudson
  • Jenkins

一些 CI 杂谈:

  • CI 测试是一种软件开发做法,团队成员 频繁集成工作 以加速调试过程。
  • 连续集成测试方法开发的应用程序将更稳定可靠,因为应用程序的模块将 定期 进行集成。
  • CI 测试能连续集成应用程序单元并在开发的 早期阶段 识别问题。
  • CI 测试能在指定时间后 自动 部署模块。
  • CI 测试可以生成项目的 运行状况报告。
  • CI 测试提供了环境以在 不同平台上 集成和测试模块。

2.Hudson 简介

简介:Hudson 运行在 Glassfish 或 Apache Tomcat 之类的服务器上,可执行 Ant Maven 项目。

优点:可轻松安装,通过 基于WEB 的 UI 配置,跟踪 jar 文件及其版本。

执行构建后操作:

  • 生成 JUnit 测试结果报告:.xml 格式保存到指定路径。  
  • 发送电子邮件通知:向开发人员发送电子邮件通知不稳定的工作版本。

3.Jenkins 简介

  • Jenkins 起初作为 Hudson 项目开发,和 Hudson 简介基本相同,但是比 Hudson 有更多的更新和补丁。
  • 要启动 Jenkins,需要访问位于端口 8080 localhost 服务器。
  • Jenkins 能将所有模块组合到一个中央储存库并将它们构建在一起。
  • 分析工作版本状态图标:
  • 分析工作版本稳定性图标:
  • 注册:
  • 使用 Jenkins 构建目,需要配置 JDK、Ant 和 Maven 路径:
  • Jenkins 上执行 CI 测试,执行以下步骤:
  • 1. 创建新项。
  • 2. 配置该项(见下图构建触发器 Build Triggers)。
  • 1+2.上面两步创建了项目,显示:
  • 3. 创建工作空间(在 Jenkins 安装文件夹中寻找 workspace)。
  • 4. 将文件复制到此工作空间(将 IDE 上的项目源文件复制到 Jenkins 安装处的 workspace)。
  • 常用的构建触发器是:
  • 在其他项目构建之后构建 / 定期构建:        
 
  • 不断循环构建,时间设置:
  • 默认情况下, Jenkins 抽取到当前用户的 .jenkins 文件夹。

三.综合测试

四.课堂记录

  • 5:00 STLC(Software Testing Life Cycle),其中“缺陷修正”和“测试执行”是一个多次往复的过程,如果这个循环一直有,说明软件代码质量很差,如果这个过程几乎没有,不能说明代码就是一点缺陷也没有,这种情况下,我们往往会认定为:测试人员的测试出现了问题,不能有效的发现BUG 
  • 7:50 测试环境的搭建也是一门艺术,需要保证被测试软件运行的环境应该能够模拟开发环境和生产环境。生产环境:顾名思义,应用于生产中的软件环境 
  • 11:20 黑盒测试:盒子是黑色的,你看不到里面的内容,内容即代码。白盒测试:盒子是白色(透明)的,你可以看到里面的内容,也就是代码
  • 13:05 测试级别:从测试颗粒度上来说,从左到右,依次颗粒度增大。颗粒:可以理解为一个测试点对应的代码量,是一个整体,不可分割的。越往右走,测试人员越应该关于最终用户的使用 
  • 34:00 java中的 "for(类型 变量 : 类型的集合)",是用来遍历类型的集合的,是for(int i=0;i < 类型的集合.length;i++)的变体 
  • 38:50 javac -cp,-cp==-classpath, 让 java解析器 去查找用户类文件和注释处理程序的位置 
  • 1:20  java -cp .;junit-4.12.jar;hamcrest-core-1.3.jar org.junit.runner.JUnitCore CalculatorTest,org.junit.runner.JUnitCore就是我们说的测试运行器(Test Runner) 
  • 1:40 要非常清楚5个注解批注的运行时间(什么时候运行),运行次数,对函数的要求(public,void,static) 
  • 3:30 assertEquals,注意:是Equals,复数 
  • 5:30 代码必须抛出特定异常,如果抛出异常,我的测试就“成功”,否则,函数有问题,测试就是“失败” 
  • 6:15 这5个异常什么时候会出现,记好异常类型,对分析程序的错误所在和原因,非常有效 
  • 6:55 为什么现在要求JDK/JRE,真正的生产环境使用64位的?32位的内存空间限制太大(学过操作系统我们都知道,32位的寻址空间限制,导致32位jdk的内存使用量不能超过4G,真正用于当前Java项目的,就更少)  
  • 8:30 例如:游戏对战逻辑,就必须要对Timeout进行限制,否则,游戏的可玩性就无从谈起了 
  • 如果直接将jre文件夹放到eclipse.exe同级别目录,即使不配置Java的环境变量(JDK1.5以上,只需要JAVA_HOME和Path),Eclipse 也能正常运行。 所以,即使安装的是32位jdk,要一个64位jre,就可以运行最新版Eclipse。 
  • 12:05 Eclipse平台(对,它其实是一个平台),提供的这个选择view的方式,现在流行的IDE基本都有类似的功能 
  • 13:35 Ignore,设置以后,不是为了从此不用这个测试方法了,往往是“还没有完成”的意思 
  • 14:35 @Test注解,接受参数时,一般就2个:expected(异常类) 和timeout(毫秒) 
  • 15:50 代码出异常了,但是符合了测试用例的设计,所以“测试成功,测试通过” 
  • 16:30 Thead.sleep(),接收的是毫秒数,通过类的静态函数sleep()控制当前代码所在的进程“休眠” 
  • 有一个小问题,我这样写,对吗? @Test(excepted=ArithmeticException.class,timeout=200) 
  • 有人可能会说,怎么计算机的题,都是些“找不同”啊?!! 对,就是因为“细节”容易犯错误 
  • 比如:assertEquals,而不是Equal,same而不是some,越简单,越觉得不值一看,越容易忽略 
  • 老师 二维数组怎么打印来着 toString也是打印hashcode 
  • 二维数组,还是双重foreach或者for吧 
  • 没有方法吗 
  • 没有太能偷懒的办法,但是,Java中的二维数组,其实还是1维数组的概念,只不过,每一个元素是特殊的,
  • 要不,Arrays.toString()方法的参数接受的也是个一维数组呢 
  • int [] intList = [4,3,2,1,2,3]; 
  • Arrays.toString(intList); 
  • 结果就是: [4,3,2,1,2,3] ,所以二维数组打印,你起码要一个for 
  • 2:30 核心、逻辑、对象、文本、集合、数字(6大类的匹配器) 
  • 现在hamcrest表达式,在PHP,Ruby,Python等语言上也都适用 
  • 7:20 尽量使用hamcrest-all 的jar包 
  • 7:30 hamcrest表达式的规律是:assertThat(期望值,匹配器),我们这节课要探讨的就是《匹配器》的使用 
  • 11:20 Eclipse中,有“workspace”的概念,活用工作空间,对管理你本地的项目至关重要。 
  • 第一行的Matchers.*,防止我们每个匹配器都要重新import 
  • 15:30 如果不是用hamcre-all的jar包,而是使用hamcrest-core的jar包,很多匹配器将会报错。 
  • 18:20 选中某一个测试用例,可以只运行它自己,而不必当前类中所有的测试用例都执行一次。 
  • 21:45 anything(),见名知意,当前匹配器是个“老好人”,只要执行,就会匹配成功 
  • 22:15 is(equalTo(x))的简写,两者等价,用来“装饰”另一个匹配器,使其更有表现力(更好理解) 
  • 23:10  assertThat("fff", is(equalTo("fff")));  // 翻译成自然语言:我认为,“fff” is equal to "fff"。确实灵活了 
  • 24:15 allOf(), 对应逻辑与。所有匹配器为true,最终才是true 
  • 25:25 anyOf(),对应逻辑或。只要有一个匹配器是true,最终就是true 
  • 25:30 逻辑匹配器,是一个可以接受多个匹配器作为参数的匹配器 
  • 27:15 not(),是逻辑取反的函数 assertThat("fff",is( not(containsString("a"))));  // 翻译成自然语言:我认为,“fff” is not contains string "a" 
  • 从这里也能看出来is()的作用,用来“装饰”语句更接近自然语言的习惯 
  • 28:25 equalTo(): 判定的是“值”相等,另外,这里是单词的单数,不是复数 
  • 28:56 hasToString(),判定测试值有toString()方法,另外,toString()的返回值跟我们的期望值一致 
  • 32:20 equalToIgnoringWhiteSpace 忽略前导和尾部空格 
  • 33:55 array()匹配器,匹配的规律是:“一一对应” 
  • 34:15 ,hasItem()匹配器,内部可以是匹配器 
  • 35:25 array()和hasItemInArray()匹配器的关系,类似于allOf()和anyOf() 
  • 36:30 ,容忍值的出现是有原因的,浮点数由于存储类型与整型不一样,所以小数点后面的数值会出现不精确的情况,所以,我们比对2个浮点值相等,是这样的:if(  floatA - floatB <   0.000001)  0.000001,就是容忍值,比如:我们要求数值的精度是小数点后面2位,那么我们使用0.0001作为容忍值,就可以保证floatA 和floatB 的相等判断是正确的 
  • 实际的软件开发中,浮点数的使用远远大于整型 
  • 其实,eclipse的项目和NetBeans的项目,目录结构很明显 
  • NetBeans的,有nbproject文件夹 
  • 【语句覆盖率= 已经执行的语句÷语句总数】 
  • 3:00 代码覆盖类型包括:语句覆盖,函数覆盖,条件覆盖,循环覆盖 
  • 5:20 5行代码,前4行执行了10此,第五行一次没执行,那么覆盖率仍然是80% 
  • 各位注意一下,需要使用Jacoco的项目,项目路径中不允许出现中文,空格,特殊字符等 
  • 各位注意一下,需要使用Jacoco的项目,项目路径中不允许出现中文,空格,特殊字符等 
  • 各位注意一下,需要使用Jacoco的项目,项目路径中不允许出现中文,空格,特殊字符等 
  • 出现这个错误消息,说明jacoco安装了,但是执行的时候有问题,找不到相关需要处理的数据,找不到的原因大概是。 
  • 这是java早起的类库常犯的错误!!! 解压包自带中文 ,这个雷,早踩晚踩都得踩 ,带中文真不是故意加的坑你们的,但是,我之前真的是提前提醒了好几次/流泪 
  • 做Java开发,项目放中文路径就是个隐藏在最后的“彩蛋”,你不知道你的代码什么时候会爆发 
  • 另外,NetBeans,Eclipse,包括Idea(大部分java 的IDE),都会默认先检测自己程序所在目录是否有“jre”这个目录,如果有,则会优先使用此目录下的jre环境。所以当你将jre目录复制到Eclipse目录下之后,你的Eclipse就变成了“绿色版” 
  • 另外,Eclipse之所以提供一个叫“work-space"的设置,就是为了让开发人员平时将代码放到它里面,然后这种”中文路径“的问题,很多时候就自动避免了 
  • 另外,这个软件内部内多http请求,所以会被杀毒软件误杀 
  • 2:00 "桩模块":有就可以,我给你输入,你给我输出,保证我的代码能够按照规划的业务执行下去,哪怕你的模块里面只有一个return语句,但是,我的业务逻辑正常执行下去了 
  • 3:00 7个业务步骤,如果等到所有的都完成了再测试,后期代码变动带来的风险会非常大,所以,我们可以使用“桩模块“的方式,先略过一些业务,保证常规业务流程的逻辑是”按照我们预期的执行了“ 
  • 6:00 "桩模块",不会增加测试代码和我们业务代码之间的耦合性 
  •  “桩模块”的实现,需要“接口机制”的支持。我们依赖的类还没有实现,但是这个类实现自一个接口,那么我们先通过自己实现一个此接口的类,返回我们“要求的固定值”,来解除对此类的依赖 
  • 在你截图的代码中,如果【EasyMock.replay(emailChecker);】没有执行的话,会怎么样?为什么? 
  • 会报错,代码里如果一个接口多次调用,会无法对接口方法进行mock,EasyMock.replay()是将Mock的行为按照mock的步骤重发一遍,在单元测试运行的时候,就能够正确的执行 
  • 缺少了“注册”,那么我们的“设置的期望值”根本不会起作用(我们姑且对运行说参数错误的情况不提),也就是说,EasyMock根本没有模拟我们的“期望” 
  • 【boolean result = register.verify(name, email);】verify中的verifyEmail()是哪个实例的?我们在这个测试代码中并没有创建verifyEmail()所隶属的实例 
  • 没有replay但是可以得到结果是为什么 
  • 得到的不是测试结果,是测试用例代码运行完毕的内容,其实是:你的测试用例并没有执行(不对也不错) 
  • 测试结果应该是:什么都没有(通过) ,告诉你:我期望是A,结果是B(不通过) 
  • 已经分清楚:断言失败和程序失败是两码事儿 ,emailChecker示例是EasyMock创建的
  • 下面的代码又用到了EasyMock的中间产物,所以,不满足EasyMock的所有步骤,测试不会正常执行(没有结果) 
  • 但是,我们其实没有创建EmailChecker的实例 
  • 当boolean result = register.verify(name, email);这句话执行的时候,EasyMock是根据我们EasyMock.expect()设置的期望值来返回结果的,并且在EasyMock.expect中,我们定义了是设置的verifyEmail()函数的传入参数。所以EasyMock会“模拟”输出,并不是真正的实例化了一个“实现了IEmailChecker”的对象 
  • 没有创建EmailChecker示例,是说没有用New的方式 
  • 建议大家:认真分析一下我们EasyMock和普通的桩模块测试代码的不同之处,好好理解一下EasyMock的工作原理 
  • 其实,EasyMock 使用了Java的反射机制,尤其是在expect()函数使用的时候 
  • EasyMock.verify(emailChecker); / / 不是参与的测试用例,而是来验证我们通过Mock创建出来的这个IEmailChecker接口对应的类的模拟实例,是否按照我们expect的条件执行了 
  •  boolean result = register.verify(name, email);  // 这句话因为前面有replay()和expect(),所以,result确实等于true,所以,assertTrue确实断言成功 
  • emailChecker是一个实例,是由EasyMock.createMock 创建的“实现了IEmailChecker接口的类的实例” 
  • 但是,这个实例隶属的类,我们不知道是哪个(这是EasyMock利用反射和下面的expect为我们构建出来的一个能返回true的代码段) 
  • 利用反射,EasyMock可以轻松知道我们要模拟的方法的传入参数个数和类型,以及返回值的类型,所以在expect执行后,EasyMock就根据传入参数的值,模拟了一个返回值的输出。 
  • 是的,虚拟类中,可能只有一个return true;因为我们的andReturn()方法要求返回true 
  • 我们可以这样假想: 
  • expect之后,有这样一段代码在我们的verifyEmail函数中: 
  • if( email=="[email protected]"){  return true;} 
  • verify()函数验证了expect的输入和andreturn之外的 
  • 我们是mock了一个IEmailChecker的实例,Registration中还得用到它呢 
  • 你删除了,怎么把这个实例传给Registration内部 
  • 对了,我们就是用这个构造函数把我们模拟的对象传进的呀 
  • 接口没有实例,实现了他的类才能创建实例 
  • 但是EasyMock可以为我们模拟出来一个实现了指定接口的类的实例(这个类我们姑且称为匿名类) 
  • 在我们EasyMock中,并没有使用a这样的类,只用到了接口IEmailChecker 
  • EJB: 商务软件的核心部分是它的业务逻辑。业务逻辑抽象了整个商务过程的流程,并使用计算机语言将他们实现。EJB 是为了"服务集群"和"企业级开发",将业务逻辑从客户端软件中抽取出来,封装在一个组件中。这个组件运行在一个独立的服务器上,客户端软件通过网络调用组件提供的服务以实现业务逻辑,而客户端软件的功能单纯到只负责发送调用请求和显示处理结果。 
  • 8:10 默认的glassfish的安装目录是:【C:\Program Files (x86)\glassfish-4.1.1\glassfish\lib\embedded】,Linux和Mac需要自己到自己解压的位置去找一下 
  • Derby:Apache Derby是一个完全用java编写的数据库,Derby是一个Open source的产品,基于Apache License 2.0分发。Apache Derby非常小巧,核心部分derby.jar只有2M,所以既可以做为单独的数据库服务器使用,也可以内嵌在应用程序中使用。 
  • JPA :JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 其一,简化现有Java EE和Java SE应用开发工作;其二,S整合ORM技术,实现统一化。 
  • Java还属于Sun的时候,Derby被开发了出来,作为Java自己的内置轻量级数据库,可是没想到让Sqlite给暗度陈仓了。不得不说,Android是一个很大的推手。 
  • 重点看一下在测试用例中,这种需要container的是如何实现“测试前准备”的,以及“上下文(context)”的概念 
  • Jenkins的功能: 自动获取代码,并编译。 
  • 测试人员或者产品发布人员,从Jenkins编译结果中下载即可。所以,jobs目录保存了当前自动构建项目的所有编译结果,(有可能也带着源代码) 
  • EasyMock需要expect和replay,Mockito需要when().thenReturn() 
  • A:只执行插入,如果主键已经存在了,插入会由于主键冲突失败
  • B:先清空,在执行插入,这样主键永远不会冲突,但是,原来表中的数据就没有了 
  • createMock:创建常规模拟对象
  • createNiceMock:可以创建随意的模拟对象(创建的不符合规范也不报错)
  • createStrictMock:最严格的,创建的稍有不规范,mock失败 
  • 逗号分隔符:简称csv,Comma separator,value 
  • csv可以用excel打开,其实是excel兼容这种格式,csv实际是文本文件,除了csv,还有tsv:tab  separator value 
  • DELETE,只删除数据集里面有的
  • DELETE_ALL,数据集对应的表中的数据,都删除 
  • 文本相应:就是请求后,返回的所有文本(html等)
  • 响应代码:200(成功),404(找不到),500(服务器内部错误),403(没有权限)
  • 响应消息:success,not found, error, access deny URL:你请求的链接 
  • 比如: 
    int a=1,b=2; 
    if(a=b){ 
    print("hello"); 
  • 在编程中,往往是:非0即为真,就因为这种错误,据说:当时的阿波罗航天飞机爆炸,跟这个有关系(程序员背锅)  这都是C的影子 
  • 不过,程序员确实干过很多高风险的事儿,比如前段时间:XX删库事件 rm -rf * 
  • 在信息安全等级比较高的公司,一般人没有rm权限 
  • 如果将来你们当老总,一定记得,不要相信你的员工会帮你考虑所有隐患 
发布了29 篇原创文章 · 获赞 21 · 访问量 1656

猜你喜欢

转载自blog.csdn.net/Lyrelion/article/details/104987992