测试矩阵

迷阵

“单元测试,集成测试,端到端测试,安全测试,性能测试,压力测试,契约测试,冒烟测试,验收测试,API测试,UI测试,兼容性测试……”

不知道你是不是像我一样,曾被这些各种各样的“测试”搞得晕头转向。作为一个有追求的开发人员,保证所写的程序、所构建的系统具备良好的质量自然是分内之事。但是面对这些千奇百怪的测试难免会望而却步,只能劝自己一句“专业的事情还是交给专业的人去做吧”,然后把测试的工作一把推给QA,闷头写自己的代码去了。

不光是测试种类众多,每个人对于某一个测试的理解也都不一样。就拿大家最熟悉的“单元测试(unit testing)”来举例,问题的关键就被聚焦到了“到底如何才算是一个单元(unit)?”有人说是一个方法,有的人说是一个类,有的人说都不对,应该是一个最小的业务单元(至少是API级别的)。还有人提出了Integration Unit Test的概念,即集成级别的单元测试。

不光是我等软件小辈,就连很多IT界的神级人物也常常为此争论不休。

古话说的好,一千个人心中有一千种单元测试,看来说的是有道理的。

列表法

(列表法)

这是昨天陪闺女写作业的时候,看到她使用了一种被称作“列表法”的方法去解一个小学2年级的逻辑题。闺女说,这种方法很神奇,原本看起来弯弯绕的问题,画个表勾勾叉叉就解决了。

随后我也查了一下:“列表法是小学数学学科中经常使用的一种方法,使用列表法可以解决许多复杂而有趣的问题。运用列出表格来分析思考、寻找思路、求解问题,经常用来解决类似于鸡兔同笼的经典问题……”

虽然我一直没有搞清楚为啥要把鸡和兔子放到一个笼子里,但回到测试迷阵的问题,好像这种小学3年级就教授的方法也能适用。

测试矩阵

(测试矩阵)

测试的种类繁多,难于理解,难于沟通。我觉得主要是在于我们将两个测试分类的维度混杂在了一起。

其中第一个维度是测试实现的层次或粒度,说白了就是在哪个层次上的测试,也可以理解成测试到底测的是哪儿。是方法?是类?是API?是单个Service?是两两Service?还是应用?还是系统?还是平台?

我们常说的单元测试,API测试,端到端测试,UI测试都是侧重于按照这种维度去分类不同的测试种类的。

但是我们在谈论这些测试的时候,其实隐含了一个概念就是他们测的是什么?也就是测试的目标。例如当我们提到上面的单元测试、API测试、端到端测试的时候其实隐含的想表达的是单元级别的功能测试,API级别的功能测试和端到端级别的功能测试。

这时候你肯定会想,这不废话么,不测功能我测什么?

这就是我想说的第二个测试分类的维度:我们测试的标的物,或是说测试的目标。如果说第一种测试维度是根据“测哪儿”区分的,那第二个维度就是根据“测什么”区分的。

例如,我们常常提到的:功能测试、集成测试、性能测试、安全测试、压力测试、兼容性测试,契约测试都是这种按照这个维度去区分不同的测试种类的,他们都不是关注于我们要测哪儿,而是更侧重于我们到底要测什么:业务功能是否正确?是否能按预期集成?契约是否被保证?安全能否达到要求?性能是否满足预期和要求?

只不过我们日常工作中,大多数情况下测试都是在验证功能是否正确,所以我们常常忽略了第二个维度,只关注于测哪儿。只有当我们去测试像性能和安全这种非功能需求的时候才会想到第二个维度,但有趣的是往往我们这时候又会忽略第一个维度,例如当我们听到有人提及性能测试的时候,并没有明确的表达测的是方法的性能、API的性能,还是UI的性能,进而导致了理解的不一致和混乱。

换个叫法

可见,之前之所以被测试迷阵困扰,其本质原因就是并没有明确区分开这两个维度,甚至将之混为一谈,从而使我们对于“XX测试”的定位和理解包括沟通都变得模糊而不准确。

如果我们不再提“单元测试”、“性能测试”这种含糊不清的概念,而是通过测试矩阵上的二维定位法,改称“方法级别的功能测试”和“API级别的性能测试”,我想我们对于测试的沟通讨论甚至学习实现将明确的多,也简单的多。


更多精彩洞见,请关注微信公众号:思特沃克

Share

QA请勿忘初心

让我们回顾一下QA与QC的区别:

Quality Assurance :The planned and systematic activities implemented in a quality system so that quality requirements for a product or service will be fulfilled.

Quality Control :The observation techniques and activities used to fulfill requirements for quality.

QA的工作涉及软件研发流程的各个环节,且涉及到每一位参与研发的人员,但质量保证工作又不涉及具体的软件研发细节,侧重于整个流程。

QC则侧重于点,利用各种方法去检查某个功能是否满足业务需求。

ThoughtWorks 的QA则是这两者的混合体,既要保证开发流程的质量,又要保证Story的功能正确。

我来ThoughtWorks已经3年了,当过BQConf讲师与主持人,参加过公司内各类测试相关活动,也阅读过邮件中分享的关于测试的话题,大部分人关注点都离不开自动化测试,面试的QA也说想到ThoughtWorks来学习高深的自动化测试,仿佛自动化测试代表了整个QA界,我反对盲目的自动化测试,确切的说反对盲目的UI自动化测试。很多QA在自动化测试海洋里迷失了自己。

我要强调自动化测试:真的没有银弹。

QA的最终价值体现

Faster Delivery Of Quality Software From Idea To Consumer

确保项目的正确性

所以自动化测试只是其中的一小部分。

如上图顶部和底部的文字是对一个QA所能带给项目的总结:“我们在开发正确的产品吗?如果是,那么我们开发的产品正确吗?”所以QA首先需要在整个项目过程中不断询问所有成员上述问题,确保团队是在开发客户所需的产品,而不是自己YY出来的产品。

确保流程的正确性

Quality is not just in the software but also in the process.

质量从来都不只是QA的职责,而是整个团队的职责。但QA如果自己都不注重,不督促组内成员改进质量,再将责任强加于整个团队,那么产品质量又何谈提升与保证。

中间的图片从一个QA的角度表明了一个用户故事的生命周期以及QA如何参与其中每个环节。

首先BA和客户将要开发的用户故事列出之后,BA与QA可以一起结对编写具体用户故事的内容,场景与验收条件,利用自己对业务以及系统的熟悉度,尽量配合BA将用户故事中坑排除掉。

所有参与用户故事kick off的角色,都应该提前了解用户故事内容。在kick off过程中,提出自己对用户故事的疑问,尽量在这个阶段解决掉业务需求上的问题。

在完成kick off后,QA可以和开发人员一起结对编写unit test以及Automated Acceptance Tests,身为一个敏捷QA,我们起码要了解团队选用的单元测试工具,熟悉项目的技术架构,这样更好的便于我们把控整个项目质量,在与开发人员结对的过程中,帮助开发人员分析业务场景的分支,来确保单元测试覆盖的是正确的场景,而不是为了交代上级随便乱写的单元测试,这也可以帮助QA熟悉代码,提高编码能力。

当开发人员完成编码工作后,这时QA、UX、BA、开发人员一起检查用户故事,是否按照用户故事来检查是否完成对应的功能。UX也可发表对用户故事UI以及交互的一些看法,有任何问题及时讨论后,将问题尽早的反馈给客户。

当开发人员交付一部分功能之后,QA就可以做常规的用户故事测试,几个迭代之后,QA开始进行跨功能需求测试和探索性测试等。根据探索性测试的结果,QA可能会调整测试策略,调整测试优先级,完善测试用例等等。

上面这些QA实践貌似已经很完美,其实还差最重要的一环:uality Analysis 。每次发布后,我们总以为我们发布了一个完美的产品,但却总能在新迭代开发过程中发现之前存在的问题,历史总是惊人的相似,为什么,没有分析总结问题,以及相应的预防手段,那么同样的问题只会重现。

同时我们也要回顾下自己在工作中真的将这些敏捷实践都应用到工作中了吗?我想或多或少的都有所欠缺。对于一个QA来说,不应循规蹈矩照搬敏捷实践。例如,在kick off中,发现开发人员、UX对用户故事涉及的场景以及内容了解不清楚,QA也可能漏掉一些测试场景,那么我们可以在kick off之前,加入一个pre-kick off的实践,留出时间,让每个角色都能够完整了解用户故事。在kick off之中,UX没有办法完整的确认页面的字体大小或者颜色等是否正确,那么在sign off之后,我们也加入一个UX-test实践,帮助UX更好解决这些问题。

所以每个项目也应都有适合自己项目的敏捷实践,发现项目存在的问题,持续改进才是最佳实践。

再来谈谈自动化测试

上面的测试金字塔对于大家来说再熟悉不过了,对于自动化测试来说最有价值的仍然是单元测试,但对于QA来说无疑最复杂的。

大部分QA或者tester,仍然以UI自动化为重心。之所以反对盲目的UI自动化测试,是因为变化频繁的UI设计,投入产生比极低,这都应该让我们重新思考下UI自动化的价值。

我们需要一个实施UI自动化的正确方式:

  • 能不用UI自动化测试就不用,梳理业务主线,只保留用户操作最频繁、交互最多的场景。
  • 根据面向对象设计的原则,构建适合项目的UI自动化框架,无论自己编写框架,还是采用开源框架。
  • 尽量采用独立测试数据,确保运行测试不受影响。例如采用mock数据库或者每次运行时还原测试数据库。

回到正题,面对自动化测试的大潮,QA应该关注什么?

  • 编码规范。这是个真实例子,开发人员对于类名命名没有用Camel-Case,造成在Linux系统中部署不成功,Python中乱使用缩进等。其实这些都可以避免,例如开发工具加入自动检查,或者在CI上加入校验编码规范的步骤,采用一些工具就可以达到目的,比如jshint,RuboCop等。
  • 结对完成单元测试或API测试等,一方面可以提高QA的编码能力,另一面可以给出开发人员一些建议,将单元测试覆盖到更多的场景。

例如,如果你们项目采用React作为前端框架,如果你不能理解React virtual dom 与jsx,当我们在写UI自动化脚本时,你会发现根本无法进行下去,日常中我们需要定位的元素全是这样:

<div class="styles__formField___1fyGy">
    <input type="text" placeholder="Email">
    <svg class="styles__formIcon___37VGd" viewBox="0 0 24 24" style="display: inline-block; fill: rgba(0, 0, 0, 0.870588); height: 24px; width: 24px; user-select: none; transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;">
        <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z">
        </path>
    </svg>
</div>

所有的页面都是js渲染出来的,如果你懂jsx,就知道只需要在对于的Component render方法中更改加入id等元素就可以搞定。

render() {
    return (
        <div>
            <input type="text" placeholder="Email" id="Email">
        </div>
    )
}

控制单元测试覆盖率,100%的单元测试覆盖率当然是最好的,但如果交付压力大,和客户商量后,我们可以尽量覆盖业务主线,而不是为了达到覆盖率延误了交付周期。

再来谈谈质量分析

作为一个QA,我们不仅要检测项目中存在的问题,也要改进团队的实践活动,更重要的是预防问题的发生。

  • 每次bug bash或相应迭代完成后,要分析统计,找出产生缺陷的环节,并采取措施防止问题再现。例如每次release或者bug bash之后,可以按照功能模块与bug类型进行统计划分,分析统计bug的成因,例如某次迭代我们bug数量激增,经调查,发现我们对某些模块的前端代码进行了重构,但缺乏相应的单元测试与集成测试,造成了我们没有及时发现bug。之后我们就对应的采取措施防止问题再现。
  • 总结分析报告,及时反馈这些信息给团队。总结分析是一个长期的任务,每次bug数量的变动,都会直接体现整个团队上次迭代的开发质量,例如bug数量减少了,可以鼓励成员再接再厉。或者某几次迭代某些模块bug呈上升趋势,那么就需要组织团队一起讨论问题根源,采取措施防止问题重现。
  • 利用代码质量分析工具,帮助我们尽早预防问题的发生。例如Sonar代码质量管理平台,可以帮助我们从代码复杂度,重复代码,单元测试覆盖率,编码规范等纬度来分析代码所存在的问题。当然也有其他的开源工具,像RubyCritic,/plato不同的语言都会有相应的工具。
  • 在线监控,利用像newrelic,airbrake等监控工具对部署在本地或在云中的Web应用程序进行监控、故障修复、诊断、线程分析以及容量计划。这样不管产品环境有任何问题,我们都能及时响应,尽早修复,减低损失。

最后让我们再看看QA应具有那些能力与技能

软技能方面包括风险控制、辅导他人、沟通能力、分析定位等。技能方面则包括缺陷管理、流程改进、测试分析、可用性测试、性能测试、安全测试等。

写在最后

回顾上述实践,其实我们可以做的更好,而不是把团队的质量全都交给自动化,回归QA的应有的初心,让我们从各个方面改进质量,带给团队更好的未来。


更多精彩文章,请关注微信公众号:软件乌托邦

Share

项目实施DevOps时,我们是如何做测试的

正如我们所知,DevOps最近几年很风靡,很多企业正在如火如荼的推行它。然而,你可曾想过,从传统到敏捷、再到DevOps,开发模式的不断革新对测试提出了怎样的挑战?

最近我们项目在实施DevOps,因此想趁热打铁,就DevOps模式下如何做测试,谈一谈自己的认知。

DevOps有什么特征

DevOps是一系列软件开发实践,强调开发人员(Dev)和运维人员(Ops)之间的沟通合作,通过自动化流程,使得软件构建、测试、发布更加快捷、频繁和可靠。

1. DevOps强调一种文化

在很多企业中,开发和运维人员通常隶属于不同部门,有着不同的工作环境,采用不同的沟通方式,使用不同的开发或运维工具,并且有着不同的业务目标,这使得他们之间形成一道参不透的墙。

DevOps实际是一种文化上的变迁,强调开发、运维、测试等环节之间的沟通合作。意在帮助这些人向着一个共同的目标努力:尽可能为公司提供更多价值。为了支持这种合作的发生,需要在团队内部文化和企业组织文化两个层面做出努力。

2. DevOps是一种实践

所谓DevOps,就是将敏捷方法延伸到Production!

DevOps主要是为了将敏捷开发实践扩展到运维阶段,进一步完善软件构建、验证、部署、交付等流程,使得跨职能团队能够完成从设计到生产支持等各环节的工作。

3. DevOps包含一系列工具链

DevOps是一种融合了一系列基本原则和实践的方法论,并从这些实践中派生出了各种工具。这些工具体现在软件开发和交付过程的不同阶段:

  • 编码:代码开发和审阅,版本控制工具、代码合并工具
  • 构建:持续集成工具、构建状态统计工具
  • 测试:通过测试和结果确定绩效的工具
  • 打包:成品仓库、应用程序部署前暂存
  • 发布:变更管理、发布审批、发布自动化
  • 配置:基础架构配置和部署,基础架构即代码工具
  • 监视:应用程序性能监视、最终用户体验

DevOps对测试提出了哪些挑战

刚参加工作时,我参与了某Audi系汽车电子的软件研发,采用的是传统瀑布开发模式。在整个项目生命周期中,前半部分设计和编码,后半部分用来测试。然而我在东家工作了两年,也没能等到产品交付到用户手上。直到去年,我们的软件才得以量产并投入市场。在这4年中,产品从未交到用户手上,因此无法验证它所带来的价值,也没有任何机会得到用户反馈从而适应变化。

后来,我又参与一个银行项目,我们采用敏捷的开发模式,全功能团队,开发测试并行,每2-3周就交付一个版本。但因为没有真正发布到生产环境,我们仍然无法及时得到有效的用户反馈。

现在,我们采用DevOps的优秀实践,开发和运维协同工作。每个迭代完成,或者每修复一个线上缺陷就立即部署到生产环境。这样,我们就能够迅速从用户处获得反馈并且快速做出响应。

通过参与传统、敏捷和DevOps的项目,我深深地感受到流程的改进对团队以及项目的产出和质量所带来的改变。

那么,这些改变究竟是对测试提出了什么样的挑战? 我认为有以下几点:

1. 频繁部署

在采用DevOps之后,我们能够根据项目具体情况做到每天甚至一天多次部署。在生产环境频繁部署软件,最大的挑战就是测试。以前,测试基本上都在开发阶段之后和产品上线之前完成。但现在,不再有充足的时间留给QA团队去发现问题再抛给开发团队来修复。那么,速度成了测试面临的一大挑战。

2. 自动化

DevOps强调将流程自动化,测试作为其中一个重要环节,势必要大规模实现自动化。因此测试人员的自动化编码能力正在面临极大的挑战。

3. 实践和反馈

敏捷提倡我们要拥抱变化,更多的是要适应需求的不断变化。虽然一部分功能性需求是明确又具体的,我们清楚的知道用户想要什么,也因此易于测试。然而,也有一些非功能性需求的验收标准没那么明确,比如:提高应用性能达到良好的用户体验。我们如何才能验证用户体验是否真的良好呢?仅仅通过性能指标吗?当然不是,满足指标只能说明一部分问题,唯有真实的用户数据和反馈才是可最靠的。

4. 协作

敏捷强调全功能开发团队的共同协作,但这仅仅止于开发阶段。而DevOps注重Dev、Ops和QA三个群体之间的密切协作。因此,良好的角色定位能够帮助测试人员将价值最大化。

我们是如何做测试的

Laurent曾经在Hiptest上发表了博客《Shift left and shift right: the testing Swing》,提出了一个有意思的测试矩阵,从四个维度进行分析,描述了当软件开发模式从瀑布到敏捷、再到DevOps转型时,测试该如何响应变化。

Laurent提出一个测试左移和右移的概念:

  • 测试左移,就是指在开发阶段之前定义测试。
  • 测试右移,就是直接在生产环境中监控,并且实时获取用户反馈。

在敏捷开发的生命周期中,我们通过每一次迭代来丰富和更新产品,以使其最大限度地符合客户对系统的需求。当时测试的关注点基本停留在开发阶段,以保证产品达到上线标准。引入DevOps之后,我们不仅要关注产品的质量是否达标,还需要使价值假设得到及时的验证。因此,我们不仅要将测试左移,在开发环境验证功能的可用性,还要进行测试右移,通过监控产品在生产环境的运作情况,来验证其价值并获得反馈,从而持续改进。基于这些理解,我在项目上做了初步的尝试并取得良好的效果。我将这些尝试和实践总结为以下几点:

1.如何保证新功能得以实现?

在开发环境,我们开发新功能,并且通过测试保证其达到产品验收标准。

首先,使用BDD(Behavior Driven Development,BDD)的方式定义用户需求,这样用特定的语言来描述用户行为,能够使各个角色(测试、开发、产品负责人、市场等)对业务价值达成一致的理解,从而使其从需求到最后的测试验证,进行高度的协作和沟通,最后交付最有价值的功能。同时,QA能够提前Review故事卡,补充验收标准。除此之外,BDD方式的用户需求可以直接指导测试,后续我会写到。

其次,采用单元测试来验证最基本的代码逻辑。在编写单元测试时,建议Dev和QA Pair工作。单元测试可以认为是编码的一部分,要对系统的代码逻辑有深入的了解,因此,Dev是最合适的人选,而QA可以帮助测试覆盖的更全面。

最后,每一个功能都要严格按照故事卡的AC(Acceptance Criteria)进行验收,并采用探索性测试方法来对新功能进行无死角测试。

2.怎样验证新功能的价值?

我们将新功能部署到生产环境以后,接下来就应该衡量业务价值是否达到预期

验证预期的一个好方法是衡量用户的行为变化。比如:在上传图片的功能后面添加了一个预览按钮,但用户却极少用它,很可能是因为用户根本不需要这个按钮,或者按钮放在了不恰当的位置导致用户不方便使用,亦或是按钮样式不够友好,导致用户没有欲望使用它。这时候,该按钮的业务价值就没有真正达到,是时候调整一下了。

3.如何确保已有功能不被破坏?

在软件开发中,任何代码都不可能完全独立存在,一行代码的变更也有可能导致系统的全面崩溃。那么,如何保证在开发新功能的同时,已有功能不被破坏?换句话说,如何做到全面的回归测试?人力是最高成本,也有现实的局限性,比如,人手不够,重复做同样的事情人会变得烦躁,手不够快导致效率低下等。因此,自动化测试才是不二选择。

将BDD需求直接转化为自动化测试用例。每个测试用例都应该讲一个关于应用程序的故事。当一个测试用例使用一致的业务术语定义时,它的可读性会比较高,且容易自动化。与此同时,上一个迭代的用例在下一个迭代就可以迅速转化为回归测试的基线。

支持BDD的工具有很多,比如:Cucumber。简单举个例子,如图:

BA用BDD方式定义用户需求,QA Review并补充AC,然后将其编写为自动化测试脚本。如果QA的编码能力较弱,可以让Dev协助完成代码实现的部分。这也充分说明了协作的意义。

最后,也是更重要的部分,测试应该集成在CI中。每一次Build或者每天都要去执行测试,验证已有功能是否完好。这样才会对没有预期到的变化产生的问题给出快速反馈。

另外,做一些性能测试、兼容性测试、和安全性测试等等。

4.怎样验证产品的可靠性?

有时候,某些缺陷并不是源于代码的错误,而是一个不好的用户体验,或者只有当数据达到一定量时才会出现,测试人员是无法模拟这种类型的测试的,因此直接在生产环境监控变得高效又可靠。通常我们需要监控两种特性:性能和可用性。

使用工具持续获取用户数据,或者使用log持续获取性能信息。这有助于监控产品部署到生产环境后是如何正确运作的。快速启用一个功能,在生产环境实时监控验证其业务价值,获取到有效且快速的用户反馈,加之拥有持续部署的能力,我们能够在出现问题的时候快速做出反应,从而使得我们的产品更加可靠。

这里实际上融入了《QA in Production》的理念。现如今,已经有很多工具和方法支持在生产环境做测试了。篇幅太长,这里就不做详细阐述了,请参考原文

到这里,再来回顾一下,我们的实践是否真的卓有成效。

  1. 用BDD的方式定义用户需求、编写测试,有益于不同角色之间的一致理解和共同协作。
  2. 自动化测试解决了频繁部署所带来的挑战,同时保证产品的整体功能持续得到回归和验证。
  3. 在线监控能有效地验证不确定需求,通过生产数据分析和预警问题的发生,并且快速获取用户反馈从而及时调整。除此之外,这一点也充分体现了Dev、QA和Ops的协作,像监控等原本只能Ops做的事,现在Dev或QA一样可以做。

写在最后

测试是一种活动,曾经我们通过它来验证产品是否达到上线标准。现在DevOps模式下,我们需要在各个阶段不断地执行测试活动,以达到产品质量的持续改进。

而QA(Tester)仅仅是一种较多进行测试活动的角色。敏捷一直强调“团队为质量负责”,测试不再是QA(Tester)的专属。DevOps模式更是对测试、尤其是自动化测试提出了更高的要求,也对QA的编码能力提出了极大的挑战。作为团队成员,每个人都有责任了解开发流程、提高测试技能,把好测试这一关。但是,测试活动作为QA(Tester)的主要职责之一,提高自动化测试技能,就是当下每个QA(Tester)最为紧急且重要的事情了。


更多精彩洞见,请关注微信公众号:思特沃克

Share

都100%代码覆盖了,还会有什么问题?

引言

(图片来自:http://t.cn/R06rQHi

很多人看到这个标题时,都会想“你都100%代码覆盖了,怎么还会有问题呢?” 让我们看一下代码例子:

public class TestCalculator {

       public Double add(Double a, Double b) {

              return a + b;}

}

再看看用junit写出的测试代码:

@Test
public void testAdd() {

       Double a = new Double(1);

       Double b = new Double(2);

       Double c = new Double(3);

       assertEquals(c, testCalculator.add(a, b));

}

当我们使用EclEmma或者Jacoco来进行覆盖测试时,对于这个类,我们将得到100%测试覆盖率。

一切看起来都那么的完美,真是这样的吗?

好吧,让我们来来看看另一个测试,当其中一个变量为null时,返回值将会是什么?

@Test
public void testAddNullPointerException() {

       Double a = new Double(1);

       Double b = null;

       Double c = new Double(3);

       assertEquals(c, testCalculator.add(a, b));

}

好了,你会发现尽管覆盖率为100%,但程序却抛出了NullPointerException。

那么肯定有人会问,这样的话单元测试覆盖率的高低都不能作为衡量项目代码质量的指标,那么我们要单元测试还有什么用?

首先,我想我们可能搞错了测试覆盖的定义。

我们先听听Martin Fowler对于测试覆盖的定义

Test coverage is a useful tool for finding untested parts of a codebase. Test coverage is of little use as a numeric statement of how good your tests are.

(图片来自:https://martinfowler.com/bliki/TestCoverage.html

他认为:把测试覆盖作为质量目标没有任何意义,我们应该把它作为一种发现未被测试覆盖的代码的手段。

所以100%的代码覆盖率还值得追求吗?

当然,这应该是每个程序员毕生的追求之一,但是如果从项目角度考虑ROI(投入产出比),对于需要快速上线的短期项目,需要注重的是让测试覆盖核心功能代码。如果你的项目是一个长期项目,那么高覆盖率是非常有必要的,它意味着高可维护性,以及更少的bug。(前提是你的测试采用TDD/BDD方式编写,我见过将测试代码写的一团糟的人,看着他的代码,我宁愿重新写一遍)

那么对于一个项目来说,覆盖率应该达到多少?

其实没有适用于所有项目的数值,每个项目都应有自己的阈值,但共性是,测试必须覆盖主要业务场景,代码的逻辑分支也必须尽可能的覆盖。

如何改进你的项目代码覆盖率?

首先我们要阅读和理解项目代码,找出其中需要测试并且与业务强相关的代码,结合sonar等代码质量管理平台,从代码编写规范、复杂度、重复代码等方面进行代码重构,进一步提高项目的可维护性与可读性。

这也意味着重构,重构的同时,你需要更多的测试来保证你重构代码的正确性。

其次要对code coverage进行度量分析,那么我们应该怎么度量code coverage?

一般来说我们从以下四个维度来度量,如上图所示:

  1. 行覆盖率(line coverage):度量被测代码中每个可执行语句是否都被执行到,但不包括java import,空行,注释等。
  2. 函数覆盖率(function coverage):度量被测代码中每个定义的函数是否都被调用。
  3. 分支覆盖率(branch coverage):度量被测代码中每一个判定的分支是否都被测试到。
  4. 语句覆盖率(statement coverage):度量被测代码是否每个语句都被执行。

所以行覆盖率的高低不能说明项目的好坏,我们要从多方面进行思考,一般我们遵循的标准应是:函数覆盖率 > 分支覆盖率 > 语句覆盖率**

代码覆盖率最重要的意义在于:

  • 阅读分析之前项目中未覆盖部分的代码,进而反推在前期QA以及相关测试人员在进行黑盒测试设计时是否考虑充分,没有覆盖到的代码是否是测试设计的盲点,为什么没有考虑到?是需求或者UX设计不够清晰,还是测试设计的理解有误。
  • 检测出程序中的废代码,可以逆向反推代码设计中不合理的地方,提醒设计/开发人员理清代码逻辑关系,提升代码质量。
  • 代码覆盖率高不能说明代码质量高,但是反过来看,代码覆盖率低,代码质量绝对不会高到哪里去,可以作为测试自我审视的重要工具之一。

结束语

单元测试的覆盖率并不只是为了取悦客户或者管理层的数据,它能够实实在在反应项目中代码的健康程度,帮助我们更好的改善了代码的质量,增加了我们对所编写代码的信心。


更多精彩洞见,请关注微信公众号:思特沃克

Share

为什么不能每周发布一次?

“看,车来了!不过貌似咱赶不上这趟车了吧?”

“啊!那快点跑,错过这趟就得再等半个小时!”

……

好无奈,可是真的赶不上也没有办法,这个场景很多人都经历过。

“这个release又是一定包就开始上hotfix,四天跟了四个,我根本没时间做回归测试!” QA小静同学抱怨道。

“每次都是定包后就开始无休止的上hotfix,咱们还不如改成每周发布一次!”Dev大鹏同学也被hotfix折磨苦了。

这是发生在蓝鲸项目中一次真实而平常的对话,跟前面赶公交车的场景有什么关系呢?

发车间隔与发布周期

发车间隔的不同带给乘客的感受会完全不同。

有些公交车很少,每半个小时一趟,有时候眼看着一辆车来了又走了,没赶上会无比懊恼,下一趟还得等上半个小时,实在着急的可能会考虑叫一辆快车赶紧走。

而有些公交车发车间隔非常短,几分钟一趟,就算错过一趟也无需等待太久,只要不是着急去救火,乘客一般不会太在乎。

项目的不同发布周期带给客户的感受也是类似的。

蓝鲸项目的发布周期跟第一种公交车发车间隔非常类似,是四周发布一次。如果功能没能在这次上线,或者有导致功能无法正常工作的缺陷,得再等一个月才能再次上线。一个月,那是多少白花花的银子啊!对于那些特别重要的功能,客户着急就会要求上hotfix,于是就出现了前面小静和大鹏的对话场景。

Hotfix是否真的能解决软件交付过程中的问题呢?其实不然。

Hotfix的引入带来很多额外的工作,影响新功能的按时交付,会打乱交付节奏,有形成恶性循环的趋势。既然这样,大家一定希望能把问题解决,而且通过公交车的例子很容易想到前面大鹏说的那个方案。

如果发布周期缩短,比如说缩短为一周甚至更短,这次没上的功能也不用那么着急的通过hotfix来上线,就能解决问题。那么蓝鲸项目为什么不一周发布一次呢?

如何才能缩短发布周期?

1. 合适的迭代计划和合理的需求切分

半个小时一趟的公交车,就算车子足够大、能够承载半个小时到达的乘客数,也会导致乘客等待时间过长,造成很多不便。要解决这种情况,可以把大型公交车换成多辆小型车,增加发车次数,缩短发车间隔。发车间隔缩短到多少,车子换成多大都是需要仔细分析和考虑的问题。

映射到蓝鲸项目,要缩短发布周期,就得有相应的小规模需求正好能够乘坐小发布周期那样的小车,因此要做好发布计划和需求切分。

这样对需求源的要求很高,需要客户那头的紧密配合。要想缩短发布周期,首先必须得有足够的、粒度合适的功能需求,能够正好安排到较短的发布周期上线。如果需求范围不能提前确定好,就没法提前做好短周期发布计划,不可能把发布周期缩短。

2. 强大的开发能力

在把乘客的需求分析清楚之后,缩短发车间隔非常关键的一点就是要有足够的安全的车和靠谱的司机。如果这一点满足不了,其他都免谈。

对应到蓝鲸项目,安全的车子和靠谱的司机组成了拥有强大开发能力的团队,包括架构支撑和人员技能。

车子就是对持续交付友好的技术架构,需要减少模块间的依赖,比如采用微服务。蓝鲸项目是一个七年之久的老项目,很多陈年依赖已经形成,要拆分不是一时半会的事情,团队正在朝着这个方向努力。

司机就是我们的开发团队,除了要有必要的开发技能外,要做到靠谱就得透彻理解持续交付的精髓,需要团队中人人都有质量意识,人人都有发布周期的紧迫感,并且能够做到高效合作,从需求划分、代码质量、测试保障等做好各个环节的工作,做好缺陷的预防和监控,不让严重缺陷流入后面阶段。

蓝鲸项目由于新人较多、人员流动大等原因,质量意识和紧迫感都有待提高。不过,在各位QA的影响下,这些问题都在改善,新人的技能也在不断的学习和实践中得到提高,但仍然不能放松警惕,需要时刻保持向前的精神面貌。

3. 充分必要的测试支撑

有了足够的安全的车和足够靠谱的司机,还得保证路况够好,这样才能做到不管到达哪个站,间隔都是相同的。

要想蓝鲸项目的持续交付能够顺利前行、一路畅通,需要严格做好质量内建工作,各层都有充分必要的自动化测试保护,减少新功能开发过程中对老功能的破坏;同时持续集成流水线也要健全,不能耽误代码提交和出包,以防影响开发和测试的进度。

蓝鲸项目开发年限已久,复杂度很高,在持续交付的路上行走的有些坎坷。目前团队正在这些方面努力采取改进措施,取得了不少进展,但确实还有不少提高的空间。

前景堪忧?

过去七年,蓝鲸的持续交付之路有些坎坷,但不应因此而失去信心。

通过跟公交系统进行对比,我们可以看到蓝鲸项目要缩短发布周期、杜绝hotfix,需要从需求切分、迭代规划、技术架构、团队能力建设和测试策略调整等多方面进行优化,才能保证持续、快速的发布节奏,这是一个系统的问题。

七年之痒已经平安度过,蓝鲸团队正在采取相应的改进措施,一旦做好了上述各方面的优化,在下一个七年,一周发布一次或者更短的发布周期都将不是梦!


更多精彩洞见,请关注微信公众号:思特沃克

Share