git项目地址:GitHub - dreamhead/geektime-todo: Geektime Todo is a demo todo project for Geektime column.
1、实现一个Todo应用
设计规范
1、对于输入参数的检测,由入口部分代码进行处理。如空字符串。
2、Repository 的问题以运行时异常 的形式抛出,业务层不需要做任何处理 如存储到Repository的时候出现问题如存储到磁盘但磁盘满了
先写单元测试代码,再写代码。
2、实现一个todo的应用下
测试覆盖率给了我们发现代码问题的机会。
在构建脚本设定的测试覆盖率是 100%,所 以,只要有测试覆盖不到的地方就会被发现。
3、程序员的测试与测试人员的测试有什么不同?
有意识地在写代码的时候编写测试,尽自己所能把各种场景考虑到。
只要团队里有合格的测试人员,他总会以你想不到的角度,发现系统中意想不到的问题
程序员的出发点是实现,而测试人员的出发点是业务。
目前大多数团队的情况是,测试人员并没有得到充分的发挥。只有程序员做好了自己的测
试,测试人员才能从日常琐碎的验证工作中解脱出来,去做更有价值的测试
如果程序员能够把自己的测试做好,很多问题就应 该被消灭在萌芽状态,根本不应该到测试同学这里。
向测试学习
1、业务视角,
但无论是什么样的测试工具,都只是提升效率的一种手段。如果没有背后的测试思维支撑,再好的工具,也是没用的。
2、学会设计测试,找到更多的测试场景。
程序员每多想到一点,软件质量就能多提高一点
测试人员设计好用例后共享,让程序员能够又一个参考去编写自己的测试。(很少有写单元测试的开发)
测试从测试场景入手,多考虑各种情况,尤其是异常情况。
4、自动化测试:为什么程序员做测试其实是有优势的?
程序员在测试上的优势所在,也就是自动化
理解自动化测试框架,主要包含两个部分:组织测试的结构以及断言。组织测试的结构最
核心的就是测试用例如何写,以及 setUp 和 tearDown 函数。而断言则是保证了我们测试
的目标。断言程序库有很多,你可以根据自己的喜好进行选择。除了断言程序库,Mock
框架的 verify 也是一种断言。
如果今天的内容你只能记住一件事,那请记住:没有断言的测试不是好测试。
05 | 一个好的自动化测试长什么样?
1、把测试写简单,简单,到一目了然,不需要证明它的正确性。
2、一个自动化测试用例,的结构分别是准备、执行、断言和清理。其中,核心的部分是执行和断
言。一个测试既不能执行太多的东西,也不能没有断言。
3、如何衡量一个测试有没有做好?-A-TRIP标准
Automatic,自动化;--需要有断言
Thorough,全面的;-测试覆盖各种场景
Repeatable,可重复的; 能够反复执行,并且结果都是一样
影响一个测试可重复性的主要因素是外部资源,
常见的外部资源包括文件、数据库、中间件、第三方服务等等。如果在测试中遇到这些外
部资源,我们就要想办法让这些资源在测试结束后,恢复原来的样子。
Independent,独立的; 测试和测试之间不应该有任何依赖。
Professional,专业的 按照代码的标准去维护。这就意味着你的测试代码也要写得清晰,比如良好的命名、把函数写小、要重构甚至要抽象出测试的基础库、测试的模式
测试用例命名:
should_ 测试场景;
should_ 测试效果 _while_ 测试条件。
06 | 测试不好做,为什么会和设计有关系?
测试不好做,实际是可测试性不好,原因是代码没设计好。
如何编写可测试的代码,最简单的回答就是让自己的代码符合软件设计原则。
编写可测试的代码,如果只记住一个通用规则,那就是编写可组合的代码。
07 | Mock 框架:怎么让测试变得可控?
Mock 框架的基本逻辑很简单,创建一个模拟对象并设置它的行为,主要就是用什么样的
参数调用时,给出怎样的反馈。
08 | 单元测试应该怎么写?
如何去写单元测试。很多团队由于多方面的原因(比如设计做得不好),导 致单元测试写得少。但为了提高代码质量以及更准确地定位问题,我们应该多写单元测 试。
单元测试最好是和实现代码一起写,以便减少后续补测试的痛苦。想写好测试,关键要做 好任务分解,否则,面对一个巨大的需求,没有人知道如何去给它写单元测试。
编写单元测试的过程,实际上就是一个任务开发的过程。一个任务代码的完成,不仅仅是 写了实现代码,还要通过相应的测试。一般而言,任务开发要先设计相应的接口,确定其 行为,然后根据这个接口设计相应的测试用例,最后,把这些用例实例化成一个个具体的 单元测试。
单元测试常见的一个问题是代码一重构,单元测试就崩溃。这很大程度上是由于测试对实 现细节的依赖过于紧密。一般来说,单元测试最好是面向接口行为来设计,因为这是一个 更宽泛的要求。其实,在测试中的很多细节也可以考虑设置得宽泛一些,比如模拟对象的 设置、模拟服务器的设置等等。
如果今天的内容你只能记住一件事,那请记住:做好任务分解,写好单元测试。
09 | 测试覆盖率:如何找出没有测试到的代码?
测试覆盖率是一种度量指标,指的是在运行一个测试集合时,代码被执行的比例
常见的测试覆盖率指标
有下面这几种:
函数覆盖率(Function coverage):代码中定义的函数有多少得到了调用;
语句覆盖率(Statement coverage):代码中有多少语句得到了执行;
分支覆盖率(Branches coverage):控制结构中的分支有多少得到了执行(比如 if 语
句中的条件);
条件覆盖率(Condition coverage):每个布尔表达式的子表达式是否都检查过 true
和 false 的不同情况;
行覆盖率(Line coverage):代码中有多少行得到了测试
JaCoCo:一个 Java 的测试覆盖率工具
JaCoCo 是 Java 社区常用的一个测试覆盖率工具JaCoCo 是 Java 社区常用的一个测试覆盖率工具
把测试覆盖率与提交过程联系起来。我们在实战中,提交之前要运行检查过程。
测试覆盖率是帮我们发现在测试中没有覆盖到的代码,也就是帮助我们在测试之外查缺补漏。
测试覆盖率实际上是一组不同指标的组合,所谓覆盖率就是运行一组测试,执行到的元素
和总的元素比例。大部分指标都比较好理解,只是条件覆盖率要求比较高,与其通过测试
覆盖那么多的条件,不如把代码本身写简单,降低测试的难度。
我以 JaCoCo 为例,给你介绍了一个测试覆盖率工具,其中的 counter 对应着测试覆盖率
的指标。在实际的项目中使用测试覆盖率工具,关键是要把它与自动化的过程结合起来,
让它不是独立的存在。每次提交,每次 CI 过程都要进行测试覆盖率的检查。
最后我们还讲到了如何通过测试覆盖率的报告找到未覆盖的代码,定位到问题之后,补齐
测试对于大多数程序员来说还是相对容易的。
如果今天的内容你只能记住一件事,那请记住:将测试覆盖率的检查加入到自动化过程之
中。
10 | 为什么 100% 的测试覆盖率是可以做到的?
要尽可能地把自己的工作数字化
要想写好测试,一个关键点是要有良好的软件设计,而且代码
本身要尽可能地消除坏味道。
我们强调的 100% 测试覆盖,主要指的是对自己编写的代码 100% 测试覆盖。这就意味
着,我们一方面要保证自己的代码完全可控,另一方面,对于影响到测试覆盖的第三方代
码要进行隔离。
要想做到 100% 的测试覆盖,技术上说,要有可测试的设计以及编写整洁
的代码,实践上看,要测试和代码同步产出。
100% 的测试覆盖并不是说代码没有问题了,而应该是程序员对自己编写代码的一种质量
保证,它是一个帮助我们查缺补漏的过程。
对于无法测试到第三方代码,要用一个薄薄的隔离层将代码隔离出去,在构建脚本中将隔
离层排除在外。有一点需要注意的是,排除脚本千万别被滥用了。
如果今天的内容你只能记住一件事,那请记住:100% 的测试覆盖率是程序员编写高质量
代码的保证。
TDD写出来的代码一般情况覆盖率都是100%
11 | 集成测试:单元测试可以解决所有问题吗?
今天我们讲了集成测试,相对于单元测试只关注单元行为,集成测试关注的多个组件协同 工作的表现。今天我们讨论了两类典型的集成问题,一种是代码之间的集成,一种是代码 与外部组件的集成。 对代码之间的集成来说,一方面要考虑我们自己编写的各个单元如何协作;另一方面,在 使用各种框架的情况下,要考虑与框架的集成。如果我们有了单元测试,这种集成主要是 关心链路的通畅,所以一般来说我们只要沿着一条执行路径,把相关的代码组装到一起进 行测试就可以了
如果涉及框架,最好是能够把框架集成一起做了,设计得比较好的框架是对于测试的支持 比较好的(比如像 Spring Boot),可以让我们很方便地进行测试。 对于外部组件的集成而言,难点在于如何控制外部组件的状态。数据库在这方面相对已经 有比较成熟的解决方案:使用单独的数据库,以及在测试结束之后进行回滚。 但大部分系统没有这么好的解决方案,尤其是第三方的服务。这时候,我们就要看有没有 合适的替代方案。对于大多数 REST API,我们可以采用模拟服务器对服务进行模拟。 通过今天的讨论你会发现,严格地说,有些代码由于基础设施的问题是不容易在自动化场 景覆盖的,这也是我们为什么要强调与框架结合的代码一定要薄,让这种代码的影响尽可 能少。这也是在减少用上层测试覆盖的工作量。
到这里,大部分的场景我们都已经可以用自动化测试进行覆盖了,我们对自己的系统已经 有了更完整的理解。其实,测试的种类还有更多,比如系统测试,把整个系统集成起来测 试;验收测试,交由业务人员或测试人员进行测试。但这些测试对于很多团队来说,已经 到了测试人员的工作范畴了。作为程序员,我们能够把单元测试和集成测试做好,整个软 件的质量已经是初步合格了。
如果今天的内容你只能记住一件事,那请记住:想办法将不同组件集成起来进行测试。
12 | 实战:将 ToDo 应用扩展为一个 REST 服务
集成测试回滚数据,保证测试的可重复 性。
13 | 在 Spring 项目中如何进行单元测试?
15 | 测试应该怎么配比?
冰淇淋模型
金字塔模型
从实用的角度上看,我们不 太可能用各种类型的测试做所有代码的覆盖,这是一种浪费。 在决定如何配比各种类型的测试前,你首先要了解各种测试的特点。比如,单元测试速度 快成本低,但覆盖面小;集成测试和系统测试覆盖面大,但速度慢成本高。
行业中目前有两种典型的测试模型:冰淇淋蛋卷和测试金字塔。二者对于测试的配比要求 刚好相反,冰淇淋蛋卷要求多写高层测试,而测试金字塔则希望多写低层测试。 行业中的最佳实践是测试金字塔,这是每个新项目都应该做到的。
对于遗留项目,我们可 以在一开始的时候,先采用冰淇淋蛋卷建立基础的安全网,在有了最低保障之后,开始向测试金字塔方向努力。 如果今天的内容你只能记住一件事,那请记住:新项目采用测试金字塔,遗留项目从冰淇 淋蛋卷出发。
16 | 怎么在遗留系统上写测试?
今天我们谈到了在遗留系统上写测试。遗留系统就是那些没有测试的系统,给遗留系统写 测试就是让一个系统恢复正常的过程。 在遗留系统上做改进,关键是要知道改进成什么样子。在一个遗留系统上写测试,不仅是 写测试,还会牵扯到写代码。 完整地给一个遗留系统写测试是比较困难的。一个实用的改进策略是,动到哪里,改哪 里。具体如何写测试,最好是测试的层次越低越好,但低层次的测试就会涉及代码耦合的 问题,而这里就需要我们对代码进行解耦。 解耦,主要是把业务代码和具体实现分开。通过提取方法,把一段耦合紧密的代码隔离 开,再创建一个新的封装类把它挪进去。如果代码里有很多具体类,我们还可以通过引入 接口进行解耦。这里面的关键是利用 IDE 给我们提供的重构功能,减少手工改代码的操 作。 如果今天的内容你只能记住一件事,那请记住:改造遗留系统的关键是解耦。
17 | TDD 就是先写测试后写代码吗?
TDD 的一个关键要素,TDD 的节奏:红 - 绿 - 重构
测试先行开发和测试驱动开发的差异就在重构上。
重构就是一个消除代码坏味道的过程。
测试驱动开发要从任务分解 开始。
为了写测试,首先“驱动”着我们把需求分解成一个一个的任务,然 后会“驱动”着我们给出一个可测试的设计,而在具体的写代码阶段,又会“驱动”着我 们不断改进写出来的代码。把这些内容结合起来看,我们真的是在用测试“驱动”着开 发。
无论你是否采用 TDD 的实践,在动手写代码之前,从测试的角度进行思考都是非常有价值 的一件事,这也是编写高质量代码的重要一环。 如果今天的内容你只能记住一件事,那请记住:从测试的视角出发看待代码。
18 | BDD 是什么东西?
今天最流行的 BDD 框架应该是 Cucumber
BDD,也就是行为驱动开发。这种思想是站在 xUnit 的框架基础之上, 让测试用例的表达更贴近业务行为。
BDD示例
核心点就是它的描述格 式:“Given…When…Then”。Given 表示一个假设前提,When 表示具体的操作, Then 则对应着这个用例要验证的结果。
BDD是站在业务的角度
想 写好 BDD 的测试用例,关键点在用业务视角描述。
了 BDD 的延伸,无论是 BDD 风格的单元测试框架,还是活文档、实例 化需求,这些都是你可以进一步探索的东西。 如果今天的内容你只能记住一件事,那请记住:技术团队要更加贴近业务。
总结
软件质量的病不在外部,而在内部。一个没有质量意识的团队只靠外部的推动很难做出高 质量软件
软件质量要想得到真正的提升,要将做到 内建质量(Build Quality In)。
内建质量,就是将质量的思考内建于软件开发的全生命周期中。
内建质量就是要把软件开发中的每一个环节都加入 质量的考虑:
业务负责人不能只要求上线日期,也要给出需求验证的业务目标和业务的验收标准; 产品经理不只是要给出产品说明,更要给出每个需求点的验收标准; 程序员不只给出代码,还要给出覆盖每行代码的自动化测试。
所谓内建质量,本质上就是用任务分解的方式,让每个环节都交付满足一定质量标准的交 付物
写代码时问问自己,这段代码应该怎么测