-
来自一个CEO的叙述在一次企业交流会上,一个公司的CEO提道,“我们公司做敏捷开发的转型有一段时间了,采用的是4周一迭代,相比之前的瀑布式开发,我们可以在每一个月就让客户看到我们的成果物,这确实为公司和客户搭建起了良好的沟通桥梁。但是,也出现了一个不好的情况,就是开发和客户之前的矛盾激化了,由于采用了迭代,所以每个月都有2天开发团队要通宵熬夜,大家苦不堪言。有个别的开发同学,骂完公司骂同事,骂完同事骂客户的,甚至连自己都不放过……”那些暴走在发布前夜的开发,你是否也遇见过呢?感同身受的无奈和愤怒来自开发的一波怒气“我晕,这谁提的代码啊,上来就白页面了,还玩个球啊!”“我倒,我本地都是好用的啊,怎么部署到环境上不行了呢!”“我去,这哪个大兄弟提了这么些代码,太能写了。我和他代码冲突多到想哭!”“我的代码让哪个孙子给覆盖了,我一下午白写了,别让我查出来谁干的哈!”“催催催,催个大脑袋啊,你行你来,不行就别说话,合代码这事是人干的活吗!”“几位大哥,我知道问题出在哪了,我这还有段代码没提交上去呢,嘿嘿,不好意思哈。”“我*!”来自领导的心酸无奈领导:“每次发布前,我都不太想去你们开发那,太压抑了!,你说咋办?”骨干:“咋办,还能咋办,换人呗。一个个都不懂咋开发还能咋办。”领导:“不是都经过笔试面试进来的吗?咋还能不懂开发呢?”骨干:“开发不是开发完了就行,得好用啊。这帮小年轻就知道埋头coding,然后扣出来的都是一堆不好用的代码,不好用倒也没啥,直接就往代码库里提交啊!”领导:“这帮小年轻这么虎吗?”骨干:“这还不算是虎的呢,还那种代码冲突了的,不管三七二十一直接就忽略冲突提交,我有好几回,一拉取最新代码,一面红色啊。”领导:“那你多带带他们,告诉他们怎么做啊!“骨干:“我都告诉过很多遍了,每次都说‘我测了啊!’,‘诶?奇怪了!’,‘不好意思哥,我忘提交了’,就这帮小年轻,我是真心带不动哈。我还是觉得以前瀑布挺好的,要不就变回去得了。“领导:“那不行啊,客户现在挺认可我们的。用敏捷一个月就能看到新做的东西。效果好,客户满意度高。你还是想想办法吧。“骨干:“我看还是换人吧。我是无法阻止别人不**的“领导:“……“问题出在哪里不知道读者读到这里是否感同身受了一下下,如果没有的话,那么恭喜你,你很幸运的加入到了一个优秀的团队或者公司。不过,可能你的IT生涯是不完整的……近些年来,整个IT圈子都在宣扬DevOps,也因此很多人都知道Dev的工作是给应用系统增加新的功能/修复Bug,而Ops的工作是要保持系统的稳定和高性能,而DevOps后就是要调和了Dev和Ops的矛盾、打破二者的壁垒,以更好的面对变化。大家满怀着希望开始了敏捷和DevOps,可是往往在新潮和流行的“上层建筑”往往发现了“形而下”的落地困难。比如Dev侧自身内部的问题。在上面举例中的开发的那波怒火和领导的无奈中,诚然有开发人员自身能力素质的问题,但这并不能算是问题,因为所有人都是从菜鸟过来了,没有人是天生的王者。“怒气”到底因为什么?为什么会有怒气呢?这个怒气几乎都来源于在团队协作中的“别人”,其实就是在沟通上产生了问题,这可以归结为工作方式方法的问题,说得更直白就是没有遵循一个良好的软件开发实践。在传统的瀑布开发的时候,开发往往会在最后“奋力一搏”完成了项目的部署工作,然后就进入了“休息”的状态,如果有bug就修改bug,如果有其他的(如文档补充,但愿你没有这样的经历)做其他的,处于被动触发这种相对轻松的姿态,所以他们从某种程度上说,是可以接受“别人”的问题的,毕竟忍耐一下就过去了。而在敏捷的迭代开发中,每次迭代都要产出潜在的可交付成果物,部署和发布是“永不止境”的,所以就没有仿佛能看到黎明吹响胜利号角的“奋力一搏”,合代码的人率先遭遇一波伤害,紧接着其他的开发一个个承受着伤害(如果出现Bug),然后大家最后再在一起承受某个或某些谁都不知道的问题带来的打击……所以往往每次的发布都是一次心志的磨练和煎熬,尤其以大工作量周期以月为单位迭代为最——这个背后的元凶其实就是“集成”!有什么好办法虽然相对传统制造业软件显得“年轻”,但是也经历了无数的大风大浪,这种问题并不专属于某些公司,这种烦恼和无奈也并非只有他们才经历过。早在软件开发的“上古”时代,软件界的大神——Martin Fowler就有过这样的经历:“我还可以生动记起第一次看到大型软件工程的情景。我当时在一家大型英国电子公司的QA部门实习。我的经理带我熟悉公司环境,我们进到一间巨大的,充满了压抑感和格子间的的仓库。我被告知这个项目已经 开发了好几年,现在正在集成阶段,并已经集成了好几个月。我的向导还告诉我没人知道集成要多久才能结束”。Martin Fowler不仅在他实习的期间认识到集成是一件很耗时并难以预测的过程,并且很多项目和团队并不把集成当回事。所以为了解决集成所带来的问题以及很多人思想上的不以为意,Martin Fowler 提出了——持续集成。持续集成 是一种软件开发实践。在持续集成中,团队成员频繁集成他们的工作成果,一般每人每天至少集成一次,也可以多次。每次集成会经过自动构建(包括自动测试)的 检验,以尽快发现集成错误。从其定义上来看,持续集成可以很好的解决开发们的“怒气”的问题,开发的怒气根本上来说就是由于在协作中缺少“沟通”所造成的,进而将问题推到了“别人”身上,试想如果整个开发的过程中,每个团队的成员彼此做的功能,或者说所提交的代码是“透明”的,那么就可以很大程度上减少这种“别人”的问题了。并且这种“透明”化的周期无需太长时间,最长为一天,最短于几个小时内,从而可以很好的解决了开发团队集成的问题,降低了交付的风险。那么,具体的落地应该有哪些呢?应该如何落地欲善其功必先利其器,在谈落地实践之前,首先看看要实现持续集成需要有哪些工具。基础工具一:版本控制系统。持续集成最基本的前提条件是对其代码库的版本控制,即:对于代码库的每一项变更,都必须被安全地存放到专有的版本控制系统中,目前最主流的版本控制系统当属Git。基础工具二:构建工具。构建工具能够通过处理应用的源代码,自动生成所需的软件(包)。软件工具的构建步骤取决于所选用的技术栈。如,Java的应用,可使用Maven作为构建工具。讲完了持续集成的定义和基础工具后,那么持续集成的过程是怎么样的呢?我们一起看看Martin Fowler是怎么带着我们玩转持续集成的吧。(以下内容来自Marin Fowler的持续集成)举个简单的例子:现在假设要完成一个软件的一部分功能,具体任务是什么并不重要,我们先假设这个 feature 很小,只用几个小时就可以完成。一开始,将已集成的源代码复制一份到 本地计算机。这可以通过从源码管理系统的 mainline 上 check out 一份源代码做到。现在拿到了工作拷贝,接下来需要做一些事情来完成任务。这包括修改产品代码和添加修改自动化测试。在持续集成中,软件应该包含完善的可自动运行的测试——自测试代码。这一般需要用到某一个流行的 XUnit 测试框架。一旦完成了修改,就会在自己的计算机上启动一个自动化 build。这会将工作拷贝中的源代码编译并链接成为一个可执行文件,并在之上运行自动化测试。只有当所有的 build 和测试都完成并没有任何错误时,这个 build 过程才可以认为是成功的。当本地build 成功后,就可以考虑将改动提交到源码仓库。但麻烦的情况在于别人可能已经在我之前修改过 mainline。这时我需要首先把别人的修改更新到自己的工作拷贝中,再重新做 build。如果别人的代码和自己的有冲突,就会在编译或测试的过程中引起错误。自己有责任改正这些问题,并重复这一过程,直到自己的工作拷贝能通过 build 并和 mainline 的代码同步。一旦本地的代码能通过 build,并和 mainline 同步,就可以把我的修改提交到源码仓库。然而,提交完代码不表示就完事大吉了。还要做一遍集成 build,这次在集成计算机上并要基于 mainline 的代码。只有这次 build 成功了,修改才算告一段落。因为总有可能会忘了什么东西在自己的机器上而没有更新到源码仓库。只有提交的改动被成功的集成了,这次工作才能算结束。如果两个开发者的修改存在冲突,这通常会被第二个人提 交代码前本地做 build 时发现。即使这时侥幸过关,接下来的集成 build 也会失败掉。不管怎样,错误都会被很快检测出来。此时首要的任务就是改正错误并让 build 恢复正常。在持续集成环境里,必须尽可能快地修复每一个集成 build。好的团队应该每天都有多个成功的 build。错误的 build 可以出现,但必须尽快得到修复。这样做的结果是你总能得到一个稳定的软件,它可能有一些 bug,但可以正常工作。每个人都基于相同的稳定代码进行开发,而且不会离得太远,否则就会不得不花很长时间集成回去。Bug被发现得越快,花在改正上的 时间就越短。上述基本上就是持续集成的过程和步骤了。那么基于此持续集成又又哪些关键的实践呢?主要有如下几个:只维护一个源代码在软件项目里需要很多文件协调一致才能 build 出产品。跟踪所有这些文件是一项困难的工作,尤其是当有很多人一起工作时。所以,一点也不奇怪,软件开发者们这些年一直在研发这方面的工具。这些工具称为 源代码管理工具,或配置管理,或版本管理系统,或源码仓库,或各种其它名字。大部分开发项目中它们是不可分割的一部分。但可惜的是,并非所有项目都是如 此。虽然很罕见,但我确实参加过一些项目,它们直接把代码存到本地驱动器和共享目录中,乱得一塌糊涂。所以, 作为一个最基本的要求,你必须有一个起码的源代码管理系统。成本不会是问题,因为有很多优秀的开源工具可用。当前较好的开源工具是 Subversion。(更 老的同样开源的 CVS 仍被广泛使用,即使是 CVS 也比什么都不用强得多,但 Subversion 更先进也更强大。)有趣的是,我从与开发者们的交谈中了解到,很多商业源代码管理工具其实不比 Subversion 更好。只有一个商业软件是大家一致同意值得花钱的,这就是 Perforce。一旦你有了源代码管理系统,你要确保所有人都知道到哪里去取代码。不应出现这样的问题:“我应该到哪里去找xxx文件?” 所有东西都应该存在源码仓库里。即便对于用了源码仓库的团队,我还是观察到一个很普遍的错误,就是他们没有把 所有东西都放在源码仓库里。一般人们都会把代码放进去,但还有许多其它文件,包括测试脚本,配置文件,数据库Schema,安装脚本,还有第三方的库,所 有这些build时需要的文件都应该放在源码仓库里。我知道一些项目甚至把编译器也放到源码仓库里(用来对付早年间那些莫名其妙的C++编译器很有效)。 一个基本原则是:你必须能够在一台干净的计算机上重做所有过程,包括checkout和完全build。只有极少量的软件需要被预装在这台干净机器上,通 常是那些又大又稳定,安装起来很复杂的软件,比如操作系统,Java开发环境,或数据库系统。你必须把 build需要的所有文件都放进源代码管理系统,此外还要把人们工作需要的其他东西也放进去。IDE配置文件就很适合放进去,因为大家共享同样的IDE配 置可以让工作更简单。版本控制系统的主要功能之一就是创建 branch 以管理开发流。这是个很有用的功能,甚至可以说是一个基础特性,但它却经常被滥用。你最好还是尽量少用 branch。一般有一个mainline就够 了,这是一条能反映项目当前开发状况的 branch。大部分情况下,大家都应该从mainline出发开始自己的工作。(合理的创建 branch 的 理由主要包括给已发布的产品做维护和临时性的实验。)一般来说,你要把build依赖的所有文件放进代码管理 系统中,但不要放build的结果。有些人习惯把最终产品也都放进代码管理系统中,我认为这是一种坏味道——这意味着可能有一些深层次的问题,很可能是无 法可靠地重新build一个产品。自动化Build通常来 说,由源代码转变成一个可运行的系统是一个复杂的过程,牵扯到编译,移动文件,将 schema 装载到数据库,诸如此类。但是,同软件开发中的其它类似任务一样,这也可以被自动化,也必须被自动化。要人工来键入各种奇怪的命令和点击各种对话框纯粹是 浪费时间,也容易滋生错误。在大部分开发平台上都能找到自动化 build 环境的影子。比如 make,这在 Unix 社区已经用了几十年了,Java 社区也开发出了 Ant,.NET 社区以前用 Nant,现在用 MSBuild。不管你在什么平台上,都要确保只用一条命令就可以运行这些脚本,从而 build 并运行系统。一 个常见的错误是没有把所有事都放进自动化 build。比如:Build 也应该包括从源码仓库中取出数据库 schema 并在执行环境中设置的过程。我要重申一下前面说过的原则:任何人都应该能从一个干净的计算机上 check out 源代码,然后敲入一条命令,就可以得到能在这台机器上运行的系统。Build 脚本有很多不同的选择,依它们所属的平台和社区而定,但也没有什么定势。尽管大部分的 Java 项目都用 Ant,还是有一些项目用 Ruby(Ruby Rake 是一个不错的 build 脚本工具)。我们也曾经用 Ant 自动化早期的 Microsoft COM 项目,事实证明很有价值。一个大型 build 通常会很耗时,如果只做了很小的修改,你不会想花时间去重复所有的步骤。所以一个好的 build 工具应该会分析哪些步骤可以跳过。一个通用的办法是比较源文件和目标文件的修改时间,并只编译那些较新的源文件。处理依赖关系要麻烦一些:如果一个目标文 件修改了,所有依赖它的部分都要重新生成。编译器可能会帮你处理这些事情,也可能不会。根据你的需要,你可能 会想 build 出各种不同的东西。你可以同时 build 系统代码和测试代码,也可以只 build 系统代码。一些组件可以被单独 build。Build 脚本应该允许你在不同的情况中 build 不同的 target。我们许多人都用 IDE,许多 IDE 都内置包含某种 build 管理功能。然而,相应的配置文件往往是这些 IDE 的专有格式,而且往往不够健壮,它们离了 IDE 就无法工作。如果只是 IDE 用户自己一个人开发的话,这还能够接受。但在团队里,一个工作于服务器上的主 build 环境和从其它脚本里运行的能力更重要。我们认为,在 Java 项目里,开发者可以用自己的 IDE 做 build,但主 build 必须用 Ant 来做,以保证它可以在开发服务器上运行。让你的Build自行测试传统意义上的 build 指编译,链接,和一些其它能让程序运行起来的步骤。程序可以运行并不意味着它也工作正常。现代静态语言可以在编译时检测出许多 bug,但还是有更多的漏网之鱼。一种又快又省的查 bug 的方法是在 build 过程中包含自动测试。当然,测试并非完美解决方案,但它确实能抓住很多 bug——多到可以让软件真正可用。极限编程(XP)和测试驱动开发(TDD)的出现很好地普及了自测试代码的概念,现在已经有很多人意识到了这种技巧的 价值。经常读我的著作的读者都知道我是 TDD 和 XP 的坚定追随者。但是我想要强调你不需要这两者中任何一个就能享受自测试代码的好处。两者都要求你先写测试,再写代码以通过测试,在这种工作模式里测试更多 着重于探索设计而不是发现 bug。这绝对是一个好方法,但对于持续集成而言它并不必要,因为这里对自测试代码的要求没有那么高。(尽管我肯定会选择用 TDD 的方式。)自测试代码需要包含一套自动化测试用例,这些测试用例可以检查大部分代码并找出 bug。测试要能够从一条简单的命令启动。测试结果必须能指出哪些测试失败了。对于包含测试的 build,测试失败必须导致 build 也失败。在过去的几年里,TDD 的崛起普及了开源的 XUnit 系列工具,这些工具用作以上用途非常理想。对于我们在 ThoughWorks 工作的人来说,XUnit 工具已经证明了它们的价值。我总是建议人们使用它们。这些最早由 Kent Beck 发明的工具使得设置一个完全自测试环境的工作变得非常简单。毋庸置疑,对于自动测试的工作而言,XUnit 工具只是一个起点。你还必须自己寻找其他更适合端对端测试的工具。现在有很多此类工具,包括FIT,Selenium,Sahi,Watir,FITnesse, 和许多其它我无法列在这里的工具。当然你不能指望测试发现所有问题。就像人们经常说的:测试通过不能证明没有 bug。然而,完美并非是你要通过自测试 build 达到的唯一目标。经常运行不完美的测试要远远好过梦想着完美的测试,但实际什么也不做。每人每天要向mainline提交代码集成的主要工作其实是沟 通。集成可以让开发者告诉其他人他们都改了什么东西。频繁的沟通可以让人们更快地了解变化。让开发者提交到 mainline 的一个先决条件是他们必须能够正确地 build 他们的代码。这当然也包括通过 build 包含的测试。在每个提交迭代里,开发者首先更新他们的工作拷贝以与 mainline 一致,解决任何可能的冲突,然后在自己的机器上做 build。在 build 通过后,他们就可以随便向 mainline 提交了。通过频繁重复上述过程,开发者可以发现 两个人之间的代码冲突。解决问题的关键是尽早发现问题。如果开发者每过几个小时就会提交一次,那冲突也会在出现的几个小时之内被发现,从这一点来说,因为 还没有做太多事,解决起来也容易。如果让冲突待上几个星期,它就会变得非常难解决。因为你在更新工作拷贝时也 会做 build,这意味着你除了解决源代码冲突外也会检查编译冲突。因为 build 是自测试的,你也可以查出代码运行时的冲突。后者如果在一段较长的时间还没被查出的话会变得尤其麻烦。因为两次提交之间只有几个小时的修改,产生这些问题 只可能在很有限的几个地方。此外,因为没改太多东西,你还可以用 diff-debugging 的技巧来找 bug。总的来说,我 的原则是每个开发者每天都必须提交代码。实践中,如果开发者提交的更为频繁效果也会更好。你提交的越多,你需要查找冲突错误的地方就越少,改起来也越快。频繁提交客观上会鼓励开发者将工作分解成以小时计的小块。这可以帮助跟踪进度和让大家感受到进展。经常会有人一开始根 本无法找到可以在几小时内完成的像样的工作,但我们发现辅导和练习可以帮助他们学习其中的技巧。每次提交都 应在集成计算机上重新构建 mainline使用每日提交的策略后,团队就能得到很多经过测试的 build。这应该意味着 mainline 应该总是处于一种健康的状态。但在实践中,事情并非总是如此。一个原因跟纪律有关,人们没有严格遵守在提交之前在本地更新并做 build 的要求。另一个原因是开发者的计算机之间环境配置的不同。结论是你必须保证日常的 build 发生在专用的集成计算机上,只有集成 build 成功了,提交的过程才算结束。本着“谁提交,谁负责”的原则,开发者必须监视 mainline 上的 build 以便失败时及时修复。一个推论是如果你在下班前提交了代码,那你在 mainline build 成功之前就不能回家。我知道主要有两种方法可以使用:手动 build,或持续集成服务器软件。手动 build 描述起来比较简单。基本上它跟提交代码之前在本地所做的那次 build 差不多。开发者登录到集成计算机,check out 出 mainline 上最新的源码(已包含最新的提交),并启动一个集成 build。他要留意 build 的进程,只有 build 成功了他的提交才算成功。(请查看 Jim Shore 的描述。)持续集成服务器软件就像一个监视着源码仓库的监视器。每 次源码仓库中有新的提交,服务器就会自动 check out 出源代码并启动一次 build,并且把 build 的结果通知提交者。这种情况下,提交者的工作直到收到通知(通常是 email)才算结束。在 ThoughtWorks,我们都是持续集成服务器软件的坚定支持者,实际上我们引领了 CruiseControl 和 CruiseControl.NET 最 早期的开发,两者都是被广泛使用的开源软件。此后,我们还做了商业版的 Cruise 持续集成服务器。我们几乎在每一个项目里都会用持续集成服务器,并且对结果非常满意。不是每个人都会用持续集成服务器。Jim Shore 就清楚地表达了为什么他更偏好手动的办法。我同意他的看法中的持续集成并不仅仅是安装几个软件而已,所有的实践 都必须为了能让持续集成更有效率。但同样的,许多持续集成执行得很好的团队也会发现持续集成服务器是个很有用的工具。许多组织根据安排好的日程表做例行 build,如每天晚上。这其实跟持续集成是两码事,而且做得远远不够。持续集成的最终目标就是要尽可能快地发现问题。Nightly build 意味着 bug 被发现之前可能会待上整整一天。一旦 bug 能在系统里呆这么久,找到并修复它们也会花较长的时间。做好持续集成的一个关键因素是一旦 mainline 上的 build 失败了,它必须被马上修复。而在持续集成环境中工作最大的好处是,你总能在一个稳定的基础上做开发。mainline 上 build 失败并不总是坏事,但如果它经常出错,就意味着人们没有认真地在提交代码前先在本地更新代码和做 build。当 mainline 上 build 真的失败时,第一时间修复就成了头等大事。为了防止在 mainline 上的问题,你也可以考虑用 pending head 的方法。当团队引入持续集成时,这通常是最难搞定的事情之一。在初期,团队会非常难以接 受频繁在 mainline 上做 build 的习惯,特别当他们工作在一个已存在的代码基础上时更是如此。但最后耐心和坚定不移的实践常常会起作用,所以不要气馁。保持快速 build持续集成的重点就是快速反馈。没有什么比缓慢的 build 更能危害持续集成活动。这里我必须承认一个奇思怪想的老家伙关于 build 快慢标准的的玩笑(译者注:原文如此,不知作者所指)。我的大部分同事认为超过1小时的 build 是不能忍受的。团队们都梦想着把 build 搞得飞快,但有时我们也确实会发现很难让它达到理想的速度。对大多数项目来说,XP 的10分钟 build 的指导方针非常合理。我们现在做的大多数项目都能达到这个要求。这值得花些力气去做,因为你在这里省下的每一分钟都能体现在每个开发者每次提交的时候。持 续集成要求频繁提交,所以这积累下来能节省很多时间。如果你一开始就要花1小时的时间做 build,想加快这个过程会相当有挑战。即使在一个从头开始的新项目里,想让 build 始终保持快速也是很有挑战的。至少在企业应用里,我们发现常见的瓶颈出现在测试时,尤其当测试涉及到外部服务如数据库。也许最关键的一步是开始使用分阶段build(staged build)。分阶段 build(也被称作 build 生产线)的基本想法是多个 build 按一定顺序执行。向 mainline 提交代码会引发第一个 build,我称之为提交 build(commit build)。提交 build 是当有人向 mainline 提交时引发的 build。提交 build 要足够快,因此它会跳过一些步骤,检测 bug 的能力也较弱。提交 build 是为了平衡质量检测和速度,因此一个好的提交 build 至少也要足够稳定以供他人基于此工作。一旦提交 build 成功,其他人就可以放心地基于这些代码工作了。但别忘了你还有更多更慢的测试要做,可以另找一台计算机来运行运行这些测试。一个简单的例子是两阶段 build。第一阶段会编译和运行一些本地测试,与数据库相关的单元测试会被完全隔离掉(stub out)。这些测试可以运行得非常快,符合我们的10分钟指导方针。但是所有跟大规模交互,尤其是真正的数据库交互的 bug 都无法被发现。第二阶段的 build 运行一组不同的测试,这些测试会调用真正的数据库并涉及更多的端到端的行为。这些测试会跑上好几小时。这种情况下,人们用第一阶段作为提交 build,并把这作为主要的持续集成工作。第二阶段 build 是次级build,只有 在需要的时候才运行,从最后一次成功的提交 build 中取出可执行文件作进一步测试。如果次级 build 失败了,大家不会立刻停下手中所有工作去修复,但团队也要在保证提交 build 正常运行的同时尽快修正 bug。实际上次级 build 并非一定要正常运行,只要 bug 都能够被检查出来并且能尽快得到解决就好。在两阶段 build 的例子里,次级 build 经常只是纯粹的测试,因为通常只是测试拖慢了速度。如果次级 build 检查到了 bug,这是一个信号,意味着提交 build 需要添加一个新测试了。你应该尽可能把次级 build 失败过的测试用例都添加到提交 build 中,使得提交 build 有能力验证这些 bug。每当有 bug 绕过提交测试,提交测试总能通过这种方法被加强。有时候确实无法找到测试速度和 bug 验证兼顾的方法,你不得不决定把这个测试放回到次级 build 里。但大部分情况下都应该可以找到合适加入提交 build 的测试。上面这个例子是关于两阶段 build,但基本原则可以被推广到任意数量的后阶段 build。提交 build 之后的其它 build 都可以同时进行,所以如果你的次级测试要两小时才能完成,你可以通过用两台机器各运行一半测试来快一点拿到结果。通过这个并行次级 build 技巧,你可以向日常 build 流程中引入包括性能测试在内的各种自动化测试。(当我过去几年内参加 Thoughtworks 的各种项目时,我碰到了很多有趣的技巧,我希望能够说服一些开发者把这些经验写出来。)在模拟生产环境中进 行测试测试的关键在于在受控条件下找出系统内可能在实际生产中出现的任何问题。这里一个明显的因素是生产系 统的运行环境。如果你不在生产环境做测试,所有环境差异都是风险,可能最终造成测试环境中运行正常的软件在生产环境中无法正常运行。自然你会想到建立一个与生产环境尽可能完全相同的测试环境。用相同的数据库软件,还要同一个版本;用相同版本的操作系统;把所有生产环 境用到的库文件都放进测试环境中,即使你的系统没有真正用到它们;使用相同的IP地址和端口;以及相同的硬件;好 吧,现实中还是有很多限制的。如果你在写一个桌面应用软件,想要模拟所有型号的装有不同第三方软件的台式机来测试显然是不现实的。类似的,有些生产环境可 能因为过于昂贵而无法复制(尽管我常碰到出于经济考虑拒绝复制不算太贵的环境,结果得不偿失的例子)。即使有这些限制,你的目标仍然是尽可能地复制生产环 境,并且要理解并接受因测试环境和生产环境不同带来的风险。如果你的安装步骤足够简单,无需太多交互,你也许 能在一个模拟生产环境里运行提交 build。但事实上系统经常反应缓慢或不够稳定,这可以用 test double 来解决。结果常常是提交测试为了速度原因在一个假环境内运行,而次级测试运行在模拟真实的生产环境中。我注意到越来越多人用虚拟化来搭建测试环境。虚拟机的状态可以被保存,因此安装并测试最新版本的build相对简单。此外,这可以让你 在一台机器上运行多个测试,或在一台机器上模拟网络里的多台主机。随着虚拟化性能的提升,这种选择看起来越来越可行。让每个人都能轻易获得最新的可执行文件软件开发中最困难的部分是确定你的软件行为符合预期。我们 发现事先清楚并正确描述需求非常困难。对人们而言,在一个有缺陷的东西上指出需要修改的地方要容易得多。敏捷开发过程认可这种行为,并从中受益。为了以这种方式工作,项目中的每个人都应该能拿到最新的可执行文件并运行。目的可以为了 demo,也可以为了探索性测试,或者只是为了看看这周有什么进展。这做起来其实相当简单:只要找到一个大家 都知道的地方来放置可执行文件即可。可以同时保存多份可执行文件以备使用。每次放进去的可执行文件应该要通过提交测试,提交测试越健壮,可执行文件就会越 稳定。如果你采用的过程是一个足够好的迭代过程,把每次迭代中最后一个 build 放进去通常是明智的决定。Demo 是一个特例,被 demo 的软件特性都应该是演示者熟悉的特性。为了 demo 的效果值得牺牲掉最新的 build,转而找一个早一点但演示者更熟悉的版本。每个人都能看到进度持续集成中最重要的是沟通。你需要保证每个人都能轻易看到系统的状态和最新的修改。沟通的最重要的途 径之一是 mainline build。如果你用 Cruise,一个内建的网站会告诉你是否正有 build 在进行,和最近一次 mainline build 的状态。许多团队喜欢把一个持续工作的状态显示设备连接到 build 系统来让这个过程更加引人注目,最受欢迎的显示设备是灯光,绿灯闪亮表示 build 成功,红灯表示失败。一种常见的选择是红色和绿色的熔岩灯,这不仅仅指示 build 的状态,还能指示它停留在这个状态的时间长短,红灯里出现气泡表示 build 出问题已经太长时间了。每一个团队都会选择他们自己的 build 传感器。如果你的选择带点幽默性和娱乐性效果会更好(最近我看到有人在实验跳舞兔)。即使你在使用手动持续集 成,可见程度依然很重要。Build 计算机的显示器可以用来显示 mainline build 的状态。你很可能需要一个 build 令牌放在正在做 build 那人的桌子上(橡皮鸡这种看上去傻傻的东西最好,原因同上)。有时人们会想在 build 成功时弄出一点噪音来,比如摇铃的声音。持续集成服务器软件的网页可以承载更多信息。Cruise 不仅显示谁在做 build,还能指出他们都改了什么。Cruise 还提供了一个历史修改记录,以便团队成员能够对最近项目里的情况有所了解。我知道 team leader喜欢用这个功能了解大家手头的工作和追踪系统的更改。使用网站的另一大优点是便于那些 远程工作的人了解项目的状态。一般来说,我倾向于让项目中发挥作用的成员都坐在一起工作,但通常也会有一些外围人员想要了解项目的动态。如果组织想要把多 个项目的 build情况聚合起来以提供自动更新的简单状态时,这也会很有用。好的信息展示方式不仅仅依赖于 电脑显示器。我最喜欢的方式出现于一个中途转入持续集成的项目。很长时间它都无法拿出一个稳定的 build。我们在墙上贴了一整年的日历,每一天都是一个小方块。每一天如果 QA 团队收到了一个能通过提交测试的稳定 build,他们都会贴一张绿色的贴纸,否则就是红色的贴纸。日积月累,从日历上能看出 build 过程在稳定地进步。直到绿色的小方块已经占据了大部分的空间时,日历被撤掉了,因为它的使命已经完成了。自动化部署自动化集成需要多个环境,一个运行提交测试,一个或多个运行次级测试。每天在这些环境之间频繁拷贝 可执行文件可不轻松,自动化是一个更好的方案。为实现自动化,你必须有几个帮你将应用轻松部署到各个环境中的脚本。有了脚本之后,自然而然的结果是你也要 用类似的方式部署到生产环境中。你可能不需要每天都部署到生产环境(尽管我见过这么做的项目),但自动化能够加快速度并减少错误。它的代价也很低,因为它 基本上和你部署到测试环境是一回事。如果你部署到生产环境,你需要多考虑一件事情:自动化回滚。坏事情随时可 能发生,如果情况不妙,最好的办法是尽快回到上一个已知的正常状态。能够自动回滚也会减轻部署的压力,从而鼓励人们更频繁地部署,使得新功能更快发布给用 户。(Ruby on Rails 社区开发了一个名为 Capistrano 的工具,是这类工具很好的代表。)我还在服 务器集群环境中见过滚动部署的方法,新软件每次被部署到一个节点上,在几小时时间内逐步替换掉原有的软件。在 web 应用开发中,我碰到的一个有趣的想法是把一个试验性的 build 部署到用户的一个子集。团队可以观察这个试验 build 被使用的情况,以决定是否将它部署到全体用户。你可以在做出最终决定之前试验新的功能和新的 UI。自动化部署加上良好的持续集成的纪律是这项工作的基础。(以上内容来自Marin Fowler的持续集成)写在最后“我的脑海中还是会浮现出第一段描述的早期软件项目。他们已经到了一个漫长项 目的末期(至少他们期望如此),但还是不知道距离真正的结束有多远。”这是来自Martin Fowler曾经历过的感受。而文中的第二段的那些开发们的“怒气”是笔者从十年前做开发的时候,所经历过的几个团队所发生过的。在集成的过程中,总会有种种无法预测的事情发生,不论是人还是事,你根本无法预测其进展从而很容易进入到迷茫地带,每一个处在迷茫地带的人都很难去做到轻松应对,久而久之开发人员会产生疲于奔命之感,导致团队无法凝聚成“拳头”打出强有力的一拳。基于这种情况,笔者认为这正是我们引入持续集成的原因所在。笔者认为Martin Fowler关于持续集成的落地实践部分已经比较详尽完全可以用来大家公共参考学习,所以没有在关公面前耍大刀,故引用于此文章。但是在我们持续集成实际的过程中一定会遇到很多问题,比如提升build效率的分段build策略或者其他实际的需求等,这都需要我们在日常工作中,通过迭代不断的来研究和完善其实践的方法,并在回顾的过程中加以讨论、分析总结,最终提升团队的研发效率。也可以使用一些大厂如华为云DevCloud作为持续集成的工具,其提供专业的一站式解决方案,方便了中小企业的DevOps落地。文章博客地址:https://bbs.huaweicloud.com/blogs/196464
-
程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。摘要在本文中,您会了解到如下的内容:先添加新功能还是先进行重构?重构到底有什么价值?如何评判这些价值?重构的时机是什么?如何进行重构?1. 先添加新功能还是先进行重构? 问题:官方资料,重构分析1.0版中。有两顶帽子,一个是添加新功能,一个是重构添加新功能时,你不应该修改既有代码,只管添加新功能,重构时你就不能再添加功能,只管改进程序结构。一次只做一件事情。这两个是否有矛盾,以哪个为准?前面有些可信材料版本不一,有的还要互相打架,是否可以统一一下?回复:关于添加新功能和重构是否矛盾的问题,是先添加新功能还是先进行重构?我们要做的是观察这两个事情哪个更容易一些,我们要做更容易的那一个。就是你不能一下子同时做这两件事情。因为同时做两件事情,会导致你工作的复杂度提升,容易出错。一般而言,重构会改变程序的设计结构改动相对来说比较大。但是因为没有功能方面的添加,所以对应的测试案例我们不需要进行修改,那对我们来说,只要能够使得现有的重构修改能够满足我们的业务测试案例就可以了。添加新功能意味着我们要添加对应的测试案例,以保证我们新的功能是可测的。这部分的修改一般会依托现有的程序结构,改动起来相对比较少,并且修改容易鉴别。在绝大多数正常情况下,我们一般是先添加功能,提交完成以后,再新的修改需求中对代码进行重构。从大的方向上来说是分两步走的,这两个任务不能混为一谈。一次只做一件事情,一次提交只包含一个任务,这是为了避免在工作中人为的增加复杂度,这个复杂度包含代码修改,审查,测试等各个方面。避免复杂度的上升,是我们在软件开发过程中时刻要谨记的一个原则。俗话说,一口吃不成胖子,心急吃不了热豆腐。做事情要一步一个脚印,稳扎稳打,步步为营。2. 重构的价值和评判效果问题:哪种类型的代码重构是高价值的?1. 在网上跑了这么多年也没啥问题,为什么要动他?2. 重构前后功能又没啥变化,当前收益是啥?3. 若是提高可维护性,可扩展性的话,怎么评判效果呢?回复:这是关于重构价值和评判结果的问题。这几个问题问的都很好。我们来看第1个问题,就是"在网上跑了这么多年也没啥问题,为什么要动"的问题?这里的关键点就在于到底有没有问题。是不是说在客户那边客户看不到问题,就算是没问题。当然不是的,在我们软件开发当中,在交付给客户以后,客户那边看到的是黑盒,他不知道我们内部的逻辑存在多少的漏洞。如果我们的内部逻辑存在很多的漏洞。假设偶然某一天,某个客户发现了一个漏洞,它可以通过这一个漏洞进入到我们的系统内部,这样进入我们的内部,会发生什么样的状况,我们可以自己想象。在公司的内部发言中专门提到了UK对我们产品的一个评价,外层是铜墙铁壁,内层是很脆弱的,客户或者黑客一旦进入到我们的内部以后,他就可以为所欲为了,从这一点上来说,我们一定要对我们现有的代码进行重构,以避免这样的问题。我们再来看第2个问题。重构前后功能又没啥变化,当前收益是什么?重构最大的收益是解决如下的问题:代码太多重复问题,单个函数体或者文件或者攻城过大的问题,模块之间耦合度太高的问题等等。以上问题归根结底就是一个问题,就是复杂度过高的问题。现在来谈一谈复杂度的问题,软件开发中的复杂度当然是越低越好。一般谈到复杂度,我们可能想到了各种逻辑上的复杂度,设计上的复杂度,实际上在软件过程中复杂度涉及到方方面面,我们来看一下,具体有哪些方面我们需要注意复杂度的问题。第一是命名规则。先举个例子,我定一个变量叫word。有的人喜欢把它写成wd。这个就增加了这个变量定义的复杂度,你从wd很难明白,这个变量是word的意思。不管是变量的命名还是函数的命名,我们都希望看到名字,我们应该能够理解这个变量或者函数大体是关联到什么样子的事情。所以谨慎的使用缩写是避免命名规则复杂度提高的重要前提。第二是程序逻辑的复杂度。线性顺序执行的复杂度为1, 出现分支以后要乘以分支的个数。分支可以是条件判断也可以是循环。所以尽可能的避免分支的出现是降低程序逻辑复杂度的重要手段。如果程序分支不可避免,要尽可能的把程序分支放到最高的逻辑层。这样做的目的是为了避免在下层处理的时候出现发散式的分支。发散式的分支会急剧的增加程序的复杂度。复杂度越高,程序越难维护,复杂度超过一定程度,人类程序员是无法处理的。第三是架构设计的复杂度。架构设计涉及到模块设计和系统设计。要尽可能的把一些公用的模块或者子系统抽取出来,比如安全相关的,日志相关的,工具相关的等等,这些公用的功能可能会被所有其他的业务模块或系统所调用。在调用这些公用功能的时候,越简单越好,并且调用者不需要关心具体的内部实现,只需要知道如何使用就可以了。这样做的目的是让程序员专注到业务代码的设计上来。第四是系统部署的复杂度。系统部署包含几个不同的阶段如开发阶段,测试阶段和生产阶段。不管是哪个阶段,部署的步骤越少越不容易出错。有些系统天然的需要很多指令的配置,如果是这样的情况,需要编写一个批处理的文件来简化外部使用者的部署步骤,把多个步骤变成一步。与部署相关联的还有集成部分。如果能够实现自动化或者从模板中创建那是非常好的状态。第五是测试的复杂度。测试分白盒测试和黑盒测试。白盒测试的复杂度直接关联着代码层级的复杂度,代码层级的复杂度越高,当然白盒测试的复杂度也就越高。白盒测试需要注意的一个重要问题是不要使白盒测试这部分的代码脱离实际业务代码的设计。也就是说白盒测试它的依附对象就是我们实际的业务代码,从架构设计上说是一个附属层,不要试图在这里使用什么软件设计艺术或者所谓的编程艺术。这种代码的风格就是简单直接,复杂度线性化。黑盒测试的复杂度来自于业务需求分析。要有非常清晰的文档说明,需要对测试步骤和预期结果写的非常清楚。第六是技术的复杂度。技术的发展趋势一般是越发展越简单,功能越强大。那么在设计和开发的过程中,要避免使用老旧的技术。关于技术框架的选择,要提前做好调研。前端选什么框架,要不要选择某些UI库,后端选什么框架,要不要选择某些程序库,原则上是为了简化我们的学习过程,提高开发效率,增强整个项目的可维护性。需要具体问题具体分析。第七是队伍结构的复杂度。队伍构成一定要短小精悍,人多不一定好办事。像亚马逊提倡的是两张披萨团队,意思是说整个团队两张pizza就能吃饱。大体估算就是10人左右的一个队伍。当然这只是一个参考指标。整个队伍的目标一定要明确。所有的人都向着那个目标迈进,分工可以不同,但是目标一定要一致。目标+分工是队伍成功运作的关键。具体来说就是把目标分成多个任务,每个任务里又可以分成小任务,那所有的人都去做对应的任务,自己让自己忙起来,而不是别人让你忙起来。我们现在来看一下第3个问题,就是如何评判重构效果的问题。在上面的分析中,我们已经了解了重构的目标和最大的收益,就是复杂度的降低。那么对应的,就是代码的重复率大大降低了,单个函数体或者代码文件或者工程过大的问题不存在或者减少了,模块之间的耦合性降低了。再进一步说,就是关于代码的可维护性和可扩展性上,我们需要关注这么几点:一是代码的可读性,我们看到现有的代码就应该可以理解代码作者的意图是什么,这样我们在修改bug的时候就更容易把握。比如函数,类或者组件的功能要单一化,命名要友好,要删除一些误导性的注释,对于一些没用的代码,要毫不客气的抛弃。二是设计模式的可参考性。设计模式的好处就是提供一种可以追寻的代码扩展轨迹,新的功能可以遵循这种轨迹模板进行添加,从现在就说一下白盒测试这一部分。测试的框架应该在项目开始阶段或者重构开始前搭起来。等部分代码成型的时候,逐步的添加必要的测试案例。测试案例的选取可以按照环形复杂度的计算方法来确定,也可以根据集成测试对应的用户需求来确定。与代码相关的测试,一般有单元测试,集成测试和系统级的测试。单元测试,一般被认为非常繁琐。单元测试的繁琐主要体现在测试案例的选取上, 如果使用全覆盖方式来选取测试案例的话,会产生大量的测试代码,以后维护起来也是一个负担。如果采用环形复杂度来选取测试案例的话,会产生适量的测试代码,但是环形复杂度的计算也是一个很大的时间开销。集成测试跟客户的实际业务需求相关。在这个过程中需要理清接口的输入与输出,以及运行路径,然后据此来设计测试案例,写出测试案例代码。开发人员一般不会拒绝写集成测试。因为她带来的好处是实实在在的,会极大的提高你的开发效率和调试效率。尤其是对于无界面的程序接口尤为重要。系统级测试是大系统中子系统之间的集成测试。这个主要包含两个方面:一个方面是有界面的自动化测试,通过这样的测试架构来模拟人类用户的使用过程,同时增加一些随机性的行为,试图能够找出系统的一些漏洞。另一种是无界面的测试,体现在多个服务系统之间的调用上或者类似浏览器自动化框架的使用上。一套完整的测试系统,可以帮助工程师提高开发效率,减少以后系统维护和重构的成本。从测试的紧迫性上来说,集成测试最为必要,系统间的测试有时候使用手工测试通过一些测试工具来代替。单元测试可以有很广阔的讨论空间,这部分要具体问题具体分析。3. 重构的时机问题:关于重构时机的说法,正确的是?添加功能时,重构能够使得未来新增特性时更快捷、更流畅在修复错误时,应该聚焦问题本身,不建议重构,可以避免引入新的问题专家Review时重构,能够传递经验,改善设计,避免或减少代码持续腐化回复:关于重构的时机问题,现在我们有三个选项,我们就分别分析一下这三个选项。第1个选项是说在添加功能的时候进行重构。这个选项的主要问题就是一个提交包含了多个任务。这属于人为的增加工作的复杂度。第1个缺点是会增加工作的难度,使得本来可以用工作量1解决的问题,变成了工作量2和3。第2个缺点是增加了代码审查的难度。本来你的提交中描述的是添加功能,结果发现里面的代码修改大部分与此描述无关。所以第1个选项排除。第2个选项是说在修复错误的时候应该聚焦问题本身,不建议重构,以避免引入新的问题。聚焦是点睛之笔。我们在做任何事情的时候,都不要忘记初心,集中精力攻克问题,不要分心。所以第2个选项是正确的。第3个选项是说专家在审查代码的时候再重构。这里面的最关键问题是专家可能并不了解代码的业务需求和应用场景。他们能够看到代码存在不好的味道,但在不了解业务场景的情况下,让专家进行重构会带来很大的风险。所以第3个选项也不正确。4. 如何进行重构?问题:如何正确的进行重构?回复:下面我们来看看如何进行重构。简单的代码重构我们都比较熟悉,比如说你通过工具就可以做一些整理,如变量重命名,函数抽取,类创建等等。现在比较头疼的一个话题就是对老产品的重构,一些老产品涉及到上千万行,上亿行的代码。关于老产品整改的问题。如果只是缝缝补补的话,可能起不到化繁为简的目的。其实做类似这种工作的话,有一个比较可行的方案。就是把现有的产品当做一个成型系统也就是现有运行的产品,不要做大的改动,顶多就是修改bug。然后以这些成型的系统为基准,去写新的系统。相当于参照一个大的白盒就写一个小的白盒,这样新的小的白盒质量上肯定比大的白盒性能上要有优势。这样子按部就班去做的话,就会比较靠谱。有朋友会说上面的做法是重写,字面意义上没错的。实际上不矛盾。区别就是重构的方式应该从下往上还是从上往下。比如说我们现在大部分的重构都理解为从下往上来做。也就是感觉这个文件里头有坏代码的味道,然后就改这个文件,这样做是没有问题的。比如现在有些教练遇到的问题,就是发现上下文不是很清晰,这个代码为什么要这么写?为什么一个文件有1万行或者3万行,这个来龙去脉不是很清楚。这个时候可能就需要从整个子模块来进行一个自上而下的分析。梳理出这个子模块的功能需求是怎样的,需要有多少个公共接口?内部公共接口的实现方式是不是应该像目前这样的?一个文件能够写成1万行或者3万行,肯定是有一定历史原因的,绝大程度是由于全局把握的编程能力不够造成的。像这种情况,如果从这个文件本身去做重构的话,难度非常之大,但是如果从上往下,从模块的整个设计角度来做重构的话,可能就容易一些。对于这样的庞然大物,最好的办法就是分而治之。首先要确定系统的功能逻辑点,针对这些逻辑点,要编排好对应的检测点,也就是说等我们完成了重构以后,我们得确保我们的重构是没有问题的,这些检测点就是做这个的,我们可以理解成集成类的测试。这些集成类的测试一定要确保可以在当前未重构之前的系统上正常运行。有了这个设施以后,我们就可以开展我们的重构工作。重构的方法有很多,比如采用比较好的工具,函数和变量的命名改变,调用方式的改变等等。这些是在现有代码的基础上进行的重构。这里我们重点说一下重写的方式来实现重构。所谓重写呢,就是另外开辟一套代码底座。甚至可以选用不同的编程语言。这种情况下重构首先要重用已有的业务逻辑,实现针对业务逻辑集成测试100%的通过率。具体不管采用哪种方式都要一个模块一个模块的进行推进。验证完成一个是一个,千万不能急于求成,试图一次性的把某些问题搞定。如果出现很多次失败,有可能会消磨掉你的自信心。所以一定要一点一点的往前推进,始终是在进步当中。采用了这种方式以后,不管当前的系统有多么的庞大,你只要坚持做下去,就一定能够把重构工作彻底完成。这个时候需要做的具体步骤可以参考如下:1. 根据功能需求定义公共接口。2. 根据公共接口写出测试案例代码。3. 这个时候可以按照测试驱动开发的理念去填充代码。4. 代码可以从现有的代码中抽取出来。5. 在抽取的过程中进行整理重构。这样,这个子模块完成以后,就可以尝试去替代现有的子模块,看看能不能在整个系统中安全的运行。 对于整个系统来说,我们又可以分成很多个子模块。然后又可以对各个子模块各个击破,最终完成对整个系统的重构。如果一开始对整个系统进行重构的话,也是可以从自上而下的角度来看的。比如说开始的时候先把所有的子模块看成一些占位符,假定他们已经完成他们的接口了。那对于整个系统来说,它本身就是一个子模块,属于提纲挈领的那个模块。这个过程,从字面意义上可以理解成重写,实际上,它也是一个重构的过程,因为我们肯定会重用这个系统本身的一些现有代码和现有的逻辑。上面我们是假定系统在已经完成的情况下进行的重构,其实重构可以贯穿于软件开发的始终。软件开发的首要目标是实现业务逻辑,能够解决客户的问题。这个目标实现以后,我们就要追求代码的干净度,复杂度能够降到最小,当前的技术能够用到最先进。所以只要有机会,我们都应该对代码和设计进行重构。结语本文针对收到的几个关于重构方面的问题作了回答,侧重点各不一样,希望能够给存在相同困惑的朋友们有所启示。
-
第一章Wings企业级单元测试自动编码引擎诞生的背景 (附Wings讲解视频和白皮书pdf下载)随着科技的飞速发展,软件系统越来越复杂,在系统测试阶段不断遇到的瓶颈,迫使行业逐步追根溯源到了单元测试阶段。软件缺陷发现得越晚,其处理费用就越呈几何激增,因此测试左移概念已经成为趋势。单元测试面临的最大问题是单元测试用例编写工作量巨大,极端情况下与开发工作量比达到1:1,甚至更高,这使大部分开发团队要么主动忽视单元测试,要么象征性的走个流程。如果可以让计算机先对被测试程序进行全局分析和深度理解,再由计算机进行全自动的完成单元测试编码,同时还能确保自动编写的代码无语法、语义错误的直接运行起来,这种用计算机智能算法全自动产生的测试编码去验证开发人员编写的源代码逻辑输入输出对错的高端测试模式,无疑是未来软件测试领域最为璀璨的“明珠”技术。国外软件诸如c++ test完成了这个领域的初步技术探索,星云测试研发的Wings(目前商用产品支持c/c++程序)产品,则大踏步完成了整体技术跨越和多方商用落地验证。Wings可以对程序参数进行深度解析,比如c++类、模板类、数组、结构体、指针、链表以及任意复杂结构的层级嵌套,同时对于面向对象的程序特性以及常用的容器库,能够完美识别和支持。对于一些void*、函数指针、模板类等无法直接静态分析进行类型确定的特殊情况,均有基于人工智能的程序分析辅助进行类型确定。Wings在基于深度参数解析的基础上,对于全局范围的程序进行理解分析后,第一步 按照内置规则,自动化构建被测程序的输入用例代码;第二步 构建测试代码用于调用被测程序的源代码;第三步 构建被测程序输出断言,完成调用被测试程序单元的全部环境准备。这个构建速度非常快,可以达到每分钟100万行左右的生成速度,编写的代码比程序开发人员手工编写的规范度高出一截,并确保100% 的语法语义正确,免去大量的调试时间。在驱动数据上,Wings实现了驱动代码和数据的分离。Wings基于深度参数解析基础上,可以根据参数的结构自动生成层级嵌套的测试数据结构,用图形界面可视化的展示给用户。用户只需要根据Wings提供的界面向导对测试数据进行填充即可,驱动程序会自动识别并读取这些数据,完成对被测试程序的调用。Wings还可以全自动生成参数捕获程序,并自动插装在被测试程序中。当被测试程序运行后,可以通过专用软件捕获程序中每个函数模块运行的具体参数值。Wings的测试代码驱动自动生成和参数捕获,相当于完成了一种全智能的闭环测试验证体系。Wings使测试数据不需要人工准备,只需要在前序轮次中通过参数捕获自动存储。若前序测试用例运行正常,那么这些数据都可以作为后续测试输入和进行校验的基础数据。第二章 单元测试自动生成技术2.1 测试左移后单元测试面临的问题测试左移后,传统的单元测试一般面临很多问题,主要如下:(1) 传统程序级测试用例的编写会耗费开发人员大量的工时,比如TDD测试驱动开发里面的单元测试无法有效实施,导致所有测试几乎全部依赖于系统级黑盒测试。程序级测试用例的开发成本是功能实现代码本身时间至少为1:1,绝大部分企业选择放弃开发程序级测试,而采用系统级测试方法。(2) 需求发生变化导致程序实现发生变化后,程序集用例也需要发生变化,和自动化面临的问题一样,单元测试本身的可维护性问题导致投入是持续的而不是一次性的,会打消其企业应用的热情。(3) 很多单元在未组装成系统的时候切入,如果需要进行测试需要进行大量的mock操作或者桩模拟,这个过程会造成单元测试的不精确性。(4) 程序集测试数据量很大,全部需要用户来进行准备无法全自动从前序系统测试过程中获取。 针对以上问题,很多业内人士提出了很多办法,例如自动生成测试用例、自动构建测试驱动、模糊测试方法等诸多方式,但是实际开发中程序输入参数十分复杂,简单的输入已经不能满足,如何能够构建复杂的参数输入,是要解决的重要问题。因此,星云测试研发了Wings产品--单元级的自动编码引擎,它不仅能够自动构建测试驱动,还能处理十分复杂的大型程序参数,帮助企业能够更好的进行单元测试。2.2 完成单元测试要素构成单元测试的要素如下:a. 测试数据b. 测试驱动代码例如针对以下程序,需要准备的测试输入以及测试代码① 全局变量:c② 参数:a、b③ 预期输出:320 30 int c = 100; int sum(int a, int b) { return a + b + c; } int driver_sum() { int a = 100, b = 20; c = 200; return sum(a, b); } TEST(test, driver_sum) { EXPECT_EQ(320, driver_sum); EXPECT_EQ(30, driver_sum); }2.3构建测试数据和测试代码遇到的技术瓶颈编写测试代码比较繁琐开发编写驱动不难,但是浪费大量的时间,并且没有创造性。编写代码是否有良好的代码规范单元测试也需要一些规范支持,例如代码规范,注释规范等,有了这些规范能够为测试人员提供很好的功能测试用例设计的逻辑思考,也为其他开发熟悉代码提供极大的便利。数据类型比较复杂通常情况下,输入参数比较复杂,嵌套层析结构比较深入,例如类包含其他类等。能否支持多组测试数据一般代码中每个循环、分支判断以及输入输出都有可能产生缺陷。因此单元测试需要很多用例,满足不同的条件分支,达到满足覆盖率。第三章 Wings的基本架构介绍3.1 Wings测试用例驱动自动生成技术的特性l Wings是智能的全自动测试用例驱动构建系统,能够在很短时间内完成对大型复杂程序的自动解析、构建。每分钟达到100万行代码的生成速率。l 可以将任意复杂类型逐步分解为基本数据类型,例如结构体嵌套,复杂类对象,c++标准容器,自定义模板类等。l 支持多层次的可视化的数据表格来对变量类型进行赋值,无需关注驱动程序本身。数据表格可以表达任意深度和多层次的数据关系,用户只需要关注表格数据,完成对输入数据的校对。l 能够区分系统数据类型和用户自定义类型,对于复杂的约定俗成系统类型可由用户自定义扁平式赋值模板,例如std::vector类型等,内部集成常用系统类型的模板。3.2 Wings的技术架构介绍Wings的技术架构:首先Wings利用代码静态分析技术,提取被测程序的主干信息并保存到Program Structure Description(PSD)结构中,PSD结构中主要存储函数的参数、全局变量以及返回值的信息,类的成员变量,成员函数,以及访问权限等信息。利用存储的信息,依据一定的编写规则,自动构建测试驱动、googletest 期望断言、测试输入、参数捕获等代码和值。 图 3.2 Wings总体架构图上述Wings的总体架构图,说明了Wings构建代码与测试输入的具体过程,整个核心技术是通过编译底层技术获取程序的信息,然后按照一定的规则构建需要的数据。第四章 程序结构信息描述程序结构信息(Program Structure Description)是指对源程序进行提取后的描述信息,主要包括类名、命名空间、类的成员变量信息、类的函数信息,以及各种类型信息等(程序结构信息,下文简称“PSD”)。描述信息的保存C语言是以一个文件为单元存储,C++提取所有的类信息存储在一个文件中。Wings的主要特性是能够对复杂的类型(结构体、类、模板类等)进行逐层展开分解到最基本数据类型(char、int、string等)。PSD结构存储在XML文件中,不同的XML文件存储不同的描述信息。Wings提取的程序结果都存储在Wingsprojects文件下的funxml与globalxml文件夹中,其中funxml文件中主要存储的文件以及作用如下:RecordDecl.xml: 结构体,联合体与类描述信息。ClassTemplateDecl.xml:模板类描述信息EnumDecl.xml:枚举描述信息funcPoint.txt: 存储函数参数为函数指针的分析结果。funcCount.txt: 存储分析到的全部函数,参数个数,参数类型信息。void.txt: 存储函数参数为void*的分析结果。filename.xml:存储c语言文件的信息。注:具体文件的描述信息,参看附录A。针对复杂类型,例如结构体类型location_s,成员变量中除了基本数据类型之外,还存在包含结构体类型的情况,如下所示的代码中,location_s中包含coordinate_s结构体,以及FILE等类型的信息,针对不同的类型进行标记区分。 源代码如下:typedef struct coordinate_s{ void(*setx)(double, int); int mInt; char *mPoi; int mArr[2][3]; void *vo; } coordinate; typedef struct location_s { int **mPoi; coordinate *coor; FILE *pf; struct location_s *next; }location; coordinate *coordinate_create(void); int coordinate_destroy(location *loc,size_t length,char *ch); void func_point(double param1, int param2); 生成对应的PSD结构信息如下图4:图4:PSD描述信息C++的主要表示类型是类,因此测试是C++以一个类为单元做测试,类主要包括类的成员变量名以及类型信息,成员变量的访问权限信息。类的成员函数分为构造函数、内联函数、虚函数等,成员函数的参数信息以及类型信息等。具体描述信息可以登陆www.codeWings.net下载Wings试用版本进行学习。第五章 Wings构建程序描述Wings通过获取到的存储信息,依据一定的编码规则,构建不同的代码程序,如驱动程序、期望断言程序、参数捕获程序等。5.1 驱动程序构建驱动程序指单元测试代码,Wings针对函数参数的类型,自动完成单元测试代码的编写。为了更详细的说明驱动程序,以一个C++类作为具体的例子说明。 类的声明如下:class BlockBuilder { public: explicit BlockBuilder(const Options* options); BlockBuilder(const BlockBuilder&) = delete; BlockBuilder& operator=(const BlockBuilder&) = delete; void Reset(); void Add(const Slice& key, std::string & value); Slice Finish(); size_t CurrentSizeEstimate() const; private: const Options* options_; std::string buffer_; int counter_; bool finished_; };针对如上BlockBuilder 类,构建一个对应的驱动类DriverBlockBuilder,驱动类中主要包含构造函数、析构函数、每个成员函数的驱动函数以及返回值函数。为了避免类中重名函数,对写入PSD需要对应生成驱动的函数进行顺序编号。驱动类声明:class DriverBlockBuilder { public: DriverBlockBuilder(Json::Value Root, int times); ~DriverBlockBuilder(); int DriverBlockBuilderReset1(int times); int Reset1Times; int DriverBlockBuilderAdd2(int times); int Add2Times; int DriverBlockBuilderFinish3(int times); void ReturnDriver_Finish3(class Wings::Slice returnType); int Finish3Times; int DriverBlockBuilderCurrentSizeEstimate4(int times); void ReturnDriver_CurrentSizeEstimate4(size_t returnType); int CurrentSizeEstimate4Times; private: Wings::BlockBuilder* _BlockBuilder; };在上述驱动类中,构造函数的作用是构造BlockBuilder的对象,用构造的对象,调用测试BlockBuilder中的成员函数。析构函数的作用是释放构建的对象。 构造函数与析构函数:DriverBlockBuilder::DriverBlockBuilder(Json::Value Root, int times){ Json::Value _BlockBuilder_Root = Root["BlockBuilder" + std::to_string(times)]; /* options_ */ Json::Value _options__Root = _BlockBuilder_Root["options_"]; int _options__len = _options__Root.size(); Wings::Options* _options_ = DriverstructOptionsPoint(_options__Root, _options__len); /* buffer_ */ std::string _buffer_= _BlockBuilder_Root["buffer_"].asString(); /* counter_ */ int _counter_ = _BlockBuilder_Root["counter_"].asInt(); /* finished_ */ bool _finished_; int _finished__value_ = _BlockBuilder_Root["finished_"].asInt(); if (_finished__value_ == 0) { _finished_ = true; } else { _finished_ = false; } _BlockBuilder = new Wings::BlockBuilder(_options_, _buffer_, _counter_, _finished_, false); } DriverBlockBuilder::~DriverBlockBuilder() { if (_BlockBuilder != nullptr) { delete _BlockBuilder; } }每个成员函数对应生成自己的驱动函数。类中的Add函数对应的驱动函数如下。Add驱动函数:int DriverBlockBuilder::DriverBlockBuilderAdd2(int times) { Add2Times = times; const char* jsonFilePath = "drivervalue/BlockBuilder/Add2.json"; Json::Value Root; Json::Reader _reader; std::ifstream _ifs(jsonFilePath); _reader.parse(_ifs, Root); Json::Value _Add2_Root = Root["Add2" + std::to_string(times)]; /*It is the 1 global variable: count Add */ int _count = _Add2_Root["count"].asInt(); count = _count; /*It is the 1 parameter: key Add2 * Parameters of the prototype:const Wings::Slice &key */ Json::Value _keykey_Root = _Add2_Root["key"]; /* data_ */ char* _keydata_; { std::string _keydata__str = _keykey_Root["data_"].asString(); _keydata_ = new char[_keydata__str.size()]; memcpy(_keydata_, _keydata__str.c_str(), _keydata__str.size()); } /* size_ */ unsigned int _keysize_ = _keykey_Root["size_"].asUInt(); Wings::Slice _key(_keydata_, _keysize_, false); /*It is the 2 parameter: value Add2 * Parameters of the prototype:const std::string &value */ string _value = _Add2_Root["value"].asString(); //The Function of Class Call _BlockBuilder->Add(_key, _value); return 0; }构成以上驱动函数的主要包括全局变量、参数、调用被测函数的构建。以上是针对BlockBuilder类的驱动类的主要信息,而构建的程序遵守google的编码规范。一些命名规则如下:Wings生成的驱动代码,存储在drivercode文件夹中。(1)driver.cc与driver.h,针对程序中使用到的一些公共函数以及头文件。(2)同一个结构体或者联合体,可能会作为多个函数的参数使用,为了避免代码的重复,Wings针对所有的结构体和联合体的不同类型,封装成不同的驱动函数或者参数捕获函数。driver_structorunion.cc 存储结构体驱动函数的代driver_structorunion.h 对应的头文件。结构体实现函数的命名规则为:DriverStruct+结构体名字+类型,其中Point代表一级指针或者一维数组,PointPoint代表二级指针或者二维数组使用。源文件的命名规则为:driver_+源文件名/类名+.cc例如:driver_nginx.cc 或 driverBlockBuilder.cc驱动函数的命名规则:Driver_+函数名+(编号)例如:Driver_ngx_show_version_info(void);DriverBlockBuilderAdd2(int times) (3)返回值的打印输出返回值的打印输出函数命名规则:Driver+Return+Print_+函数名。例如:DriverReturnPrint_ngx_show_version_info();(4)用户源代码中的main函数自动进行注释,重新生成一个main函数文件,来进行测试。Wings会生成驱动main的主函数文件为:gtest_auto_main.ccWings主要针对参数进行逐层展开,解析到最底层为基本类型进行处理。驱动的赋值部分,主要就是针对基本类型进行处理。(注:特殊类型,比如FILE等,后面会详细讲解如何赋值)以int类型举例,一般程序构成int的主要组成大概包括以下情况:int p; int *p; int **p; int ***p;int p[1]; int p[2][3]; int p[1][2][3];int(*p)[]; int(*p)[][3]; int *(*p)[]; int (**p)[];int *a[]; int **a[]; int *a[][3]; int (*a[])[];Wings会针对基本类型的以上15种类型,进行不同的赋值。构建完驱动程序之后,要对驱动程序进行运行,Wings构建googletest的框架,来进行测试。下面我们将针对期望断言的googletest程序进行构建。5.2 googletest程序的构建在构建完单元测试驱动程序之后,Wings将调用googletest的框架,完成对返回值的期望值验证代码,并且输出测试结果。Wings运行单元测试过程中,会构建返回值的保存代码,将程序的返回值结果进行存储,然后读取用户输入的预期值,进行结果对比,判断是否通过。针对具体的类,对应生成gtest类,每个gtest类中对每个函数构建期望代码。例如针对BlockBuilder类,生成的gtest类为 GtestBlockBuilder。GtestBlockBuilder类的声明:class GtestBlockBuilder : public testing::Test { protected: virtual void SetUp() { const char* jsonFilePath = "../drivervalue/RecordDecl.json"; Json::Value Root; Json::Reader _reader; std::ifstream _ifs(jsonFilePath); _reader.parse(_ifs, Root); driverBlockBuilder = new DriverBlockBuilder(Root, 0); } virtual void TearDown() { delete driverBlockBuilder; } DriverBlockBuilder* driverBlockBuilder; }; BlockBuilder类中的每个函数对应一个gtest函数,每个函数负责调用DriverBlockBuilder编写的驱动函数,对于包含返回值信息的函数,则对应生成具体的期望值对比。 期望对比函数CurrentSizeEstimate:TEST_F(GtestBlockBuilder, DriverBlockBuilderCurrentSizeEstimate4) { const char* jsonFilePath = "drivervalue/BlockBuilder/CurrentSizeEstimate4.json"; Json::Value Root; Json::Reader _reader; std::ifstream _ifs(jsonFilePath); _reader.parse(_ifs, Root); for (int i = 0; i < BLOCKBUILDER_CURRENTSIZEESTIMATE4_TIMES; i++) { driverBlockBuilder->DriverBlockBuilderCurrentSizeEstimate4(i); Json::Value _CurrentSizeEstimate4_Root = Root["CurrentSizeEstimate4" + std::to_string(i)]; /* return */ unsigned int _return_actual = _CurrentSizeEstimate4_Root["return"].asUInt(); /* return */ unsigned int _return_expected = _CurrentSizeEstimate4_Root["return"].asUInt(); /* return_expected */ EXPECT_EQ(_return_expected, _return_actual); } }最后调用自动构建的main函数运行整个单元测试过程。5.3 参数捕获程序构建参数捕获是指在程序运行过程中获取程序的变量信息,主要包括函数的参数、全局变量、返回值等。Wings自动构建获取参数的程序,利用插装技术,将构建的捕获函数插入源代码中对应的位置,将获取的具体信息,写入值文件,可以将获取的数据作为单元测试的输入,在代码发生变更后,利用相同的输入判断是否得到相同的输出,进行回归测试。Wings的参数捕获代码存储在paramcaputrecode文件夹中。其中命名规则同驱动格式一样,将所有的driver替换为param即可。Wings针对每个类生成一个对应的参数捕获类,而参数捕获类中针对每个函数生成对应的捕获参数、全局变量以及返回值的函数。c++ 中类的成员变量是私有,无法从外部获取,Wings利用插桩技术,对每个类插入一个捕获函数,来获取类的私有成员变量。参数捕获类ParamCaptureBlockBuilder:class ParamCaptureBlockBuilder { public: ParamCaptureBlockBuilder(); ~ParamCaptureBlockBuilder(); void ParamCapture_Reset1(); void GlobalCapture_Reset1(); void ReturnCapture_Reset1(); void ParamCapture_Add2(const Wings::Slice &key, const std::string &value); void GlobalCapture_Add2(); void ReturnCapture_Add2(); void ParamCapture_Finish3(); void GlobalCapture_Finish3(); void ReturnCapture_Finish3(class Wings::Slice returnType); void ParamCapture_CurrentSizeEstimate4(); void GlobalCapture_CurrentSizeEstimate4(); void ReturnCapture_CurrentSizeEstimate4(size_t returnType); };具体的捕获函数不再详细说明,具体信息可以在Wings官网下载试用版本查看。第六章 Wings类型以及面向对象语法特性的支持Wings能够支持任意的类型以及面向对象的语法特性。类型支持:l 基本类型,int、double、float、std::string等l 任意复杂的结构类型,结构体、联合体、枚举、链表、多级指针、数组、树、图等l 任意复杂的类对象,标准库容器、自定义模板类特殊模板:n 区分用户自定义的类型与系统变量类型(标准库头文件中包含的类型)n 类的运算符重载函数n void *与函数指针n 识别类中包含delete与default关键字的函数进行特殊处理语法支持:u 处理static函数、保护和私有函数u 类中定义私有结构体u 类中包含delete与default关键字的函数进行特殊处理u 多态与复杂的类继承6.1链表针对链表类型,采用比较灵活的赋值方式,考虑到实际应用中的一些因素,针对链表类型,默认赋值两层结构,在实际测试过程中,用户可依据需要自动添加节点。6.2 标准库容器Wings能够支持c++的标准库容器,能够对容器进行逐层展开,利用不同容器的标准赋值函数进行赋值以取值。其他类似的容器例如QT中的容器以及boost库中的相关容器,我们在继续支持。6.3 自定义模板类一些用户自定义的模板类类型,Wings能够识别是用户自定义的模板类,Wings依据实际程序中的赋值类型,进行处理。6.4 void*与函数指针Void*与函数指针在实际程序中可以作为函数参数或者结构体与类的成员变量,针对不确定的赋值类型,Wings提供了具体的解决办法:① 利用编译底层技术,对源程序静态分析,获取真实类型,对真实类型进行赋值② 由用户在数据表格界面配置实际类型6.5 特殊模板在实际的代码程序中,会存在一些类型无法使用通用的模式全部展开,如一些系统变量(FILE、iostream)、第三方库以及一些用户需要特殊赋值。Wings是如何针对以上特殊类型进行赋值,举例如下:struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };步骤如下:a. 识别sockaddr_in为系统变量类型,特殊标记b. 检测程序中的所有系统变量类型,显示在模板界面c. 用户配置特殊变量,如sin_familyd. 构建sockaddr_in对象e. 调用模板,生成驱动模板配置如下:图:6.5模板配置 第七章 数据表格Wings目前测试用例数据采用随机生成的方式,支持int、char、double、float、bool、char*类型。数据表格可以任意编辑以上类型的数值。(1)Wings数据表格将会针对参数进行展开,假如参数类型为结构类型,数据表格将分层展开结构的类型,到基本类型。(2) 针对基本类型的指针类型,例如int *p;Wings处理为不定长度的一维数组类型,int **p;处理为不定长度的二维数组类型,默认长度为3,数据表格界面可以点击进行添加和删除数据。(3) 针对不定长度的数组作为函数参数,例如int p[];Wings默认长度为1,用户依据需求,在数据表格界面进行任意添加和修改即可。图 7-1数据表格展示 附录A表一:type属性ZOA_CHAR_S/ZOA_UCHAR/ZOA_INT/ZOA_UINT/ZOA_LONG/ZOA_ULONG/ZOA_FLOAT/ZOA_UFLOAT/ZOA_SHOTR/ZOA_USHORT/ZOA_DOUBLE/ZOA_UDOUBLE基本类型StructureOrClassType结构体类型ZOA_FUNC函数指针类型ZOA_UNION联合体类型ZOA_ENUM枚举类型ClassType类类型 表二:basetype属性BuiltinType基本类型ArrayType数组类型PointerType指针类型StructureOrClassType结构体类型UnionType联合体类型EnumType枚举类型FunctionPointType函数指针类型 表三:其他属性Name代表结构体、类、联合体名字NodeType代表链表类型parmType代表函数参数类型parNum代表函数参数个数SystemVar代表此类型为系统头文件类型value代表枚举类型的值bitfield代表位域类型所占字节returnType代表返回值类型Field类成员变量Method类构造函数paramName类构造函数参数名paramType类构造函数参数类型TemplateArgumentTypeSTL结构参数类型WingsTemplateArgumentSTL结构嵌套参数名字TemplateArgumentValueSTL结构中参数为具体值FunctionModifiers函数访问权限FunctionAttribute函数是extern或者static函数FuncClassName函数所属类OperatorFundecl重载运算符函数Operator重载运算符类型
-
第一章 精准测试诞生的背景(附完整版精准测试讲解视频和白皮书pdf下载)20年前(2000年),上网是一件很酷的事,叫做“网上冲浪”,主要是几个门户网站占据绝大多数注意力;20年后(2020年),我们已经全方位“浸泡”在软件的海洋里,很多大型软件系统已经超过亿行,架构模型越来越复杂。未来的20年(2020-2040年),随着各种智能应用的层出不穷,软件系统内部逻辑会不可逆转的越来越复杂,而外部操作会越来越“傻瓜”。用户一个眼神或者一动脑,就能够切换出不同的功能需求。同时中国的软件产品化进程,近些年来正在蓬勃的进行,大量的公司开始研发自己的产品。伴随着关键核心软件的国产替代,使得原本应用级难度的开发正在向高复杂度和高质量的产品型研发转型。这个转型的过程,注定将产生对于高端测试工具的巨大需求。软件缺陷发现得越晚,其处理费用即呈几何性激增,而不是线性增长。如何从庞大复杂的系统中迅速及时地找到故障所在,一直是行业的一大难点。无法对大型软件进行有效的质量把控,就无法真正构建与维护大型软件。目前国内软件测试基本处于两种状态:一是绝大多数企业采用功能(黑盒)测试,二是小部分对软件产品有高可靠性要求的软件,企业会使用代码级的白盒测试工具。但这两种传统测试办法在目前的软件日趋智能化复杂化下,弊端越来越明显。功能(黑盒)测试技术**,使墨菲定律在所难免。**由于无法获知程序内部逻辑结构,这种测试办法对软件可靠性要求不高的应用来讲问题不是很大,但是对于大型金融机构、智能工业、航天军工等关键系统,就意味着时刻携带隐形的巨大风险。为此,功能测试后期需要极高的人力投入才能完成复杂逻辑的用例分析和设计。然而对于黑盒测试来说,程序越大,杀虫剂效应越明显。而行业内当作银弹的自动化测试,当自动化程序本身规模扩大以后,它的维护本身就存在了很严重的问题。低效的黑盒测试状态,最终将影响和制约未来整体软件发展。代码级(白盒)测试工具一般重点应用在研发阶段的单元测试上,满足了客户的部分高可靠性需求,但由于其价格高昂、技术老化,仅适合于小规模迭代瀑布式开发的软件,无法完成复杂的系统级别的测试以及分布式基于云的测试,更不适应敏捷迭代的开发模式。另外白盒测试工具基本都是国外产品,通常这些产品无法完成深度的定制化功能实现以及快速的用户响应,代码安全更是一个较大的问题。所有先进的科技都是具备前瞻性眼光的。随着国内军民各项大型核心软件系统的上马,研发一种面向高复杂度大型软件、自主可控的高性能智能精准测试平台的必要性,逐渐凸显。在这个时代背景下,2012年初,星云测试团队开始了筚路蓝缕、心无旁骛的研发征程。精准测试是个交叉学科,里面涉及到编译器、测试分析、图形技术、高性能通信与存储,软件的研发等多项底层技术。经历无数个不眠之夜对技术难点突破的煎熬与最佳解决方案的反复推敲,星云精准测试产品在诸多方面率先实现了重大技术创新,成功突破了白盒测试使用难度大、价格高昂的桎梏,有效消弭了国外高端测试产品垄断的壁垒。星云精准测试产品更偏向于软件测试业界的“灰盒测试”,即用简单的黑盒操作办法,可以同时得到单元级和系统级的精准测试数据。“星云精准测试”不负众望,在众多性能上大幅超越国外进口高端白盒测试工具产品,并在数据追溯、覆盖率可视化、智能回归、智能缺陷定位、分布式数据穿透与追踪等特性上有突出贡献。星云精准测试,既继承了传统功能测试前期的高效率运行区间,又能在后期通过系统的数据,让开发、测试充分协同,完成全程高效的测试与数据分析。(1)将测试团队的价值放大,能够将开发与测试更加紧密的连接起来,互为支撑。(2)采用精准、可信的测试技术,测试管理的难度大幅度降低。(3)降低企业对人员的过度依赖,通过系统适应人员的变更。(4)为企业实现测试数据资产的有效积累和分析,打下坚实基础。“星云精准测试VIP大企业离线版云平台”在整体测试试功能上的优异特性,成功获得了一批重要大型企业的高度认可及产品采购。星云测试的首发版本为:穿线测试 ThreadingTest,2014年6月6日上线,侧重于系统级白盒测试技术,测试用例和代码逻辑的双向追溯技术,测试示波器技术,覆盖率可视化技术。2015年8月6日,“穿线测试”正式更名为“星云测试”。在继承穿线测试整体技术上,星云精准测试增强了回归测试用例的自动选取技术,缺陷最后执行时序分析、智能缺陷定位、敏捷环境下多版本白盒测试数据的聚合、聚类分析、结合代码结构与动态数据的测试漏洞检出、代码安全特性,全面的测试管理特性等几十种优秀功能。目前有“星云精准测试VIP大企业离线版云平台”、“星云精准测试PASS在线云平台www.teststars.cc”、“全自动测试用例驱动生成系统Wings”等多种工具产品。星云精准测试旗下产品平台有Horn、Paw、Shell、Wings等系列产品。适用语言和平台暂为:为响应广大用户的需求,目前正在进一步扩展适应的语言和平台覆盖面。图1-1 精准测试在大型系统的效率运行分析星云精准测试,既保证了传统功能测试前期的高效率运行区间,又能在后期通过系统的数据,让开发、测试充分协同,完成全程高效的自动化精准测试。第一章 精准测试技术体系对软件测试技术的升级解析精准测试技术体系经过近6年的商业项目实践日臻成熟,大量商业应用案例的正面积极反馈,表明它已经成为新的软件测试技术方向体系。星云精准测试灵巧详细的功能设计和高度可靠的产品内核得到了市场的广泛认可,对于国际上原有的白盒测试工具来讲,是一种质的飞跃。2012年,精准测试的商用工具正式进入设计和研发阶段。与引进国外技术不一样,精准测试的核心技术体系是星云测试团队在国内的原创设计和研发。产品经过3年的悉心研发、反复内测、不断优化后,2014年精准测试在CSTQB会议上,发布了第一个商用版本ThreadingTest,引起了很大反响。6年前,行业上还全面沉浸在黑盒测试的大氛围中,从未见过精准测试的超前的运行方法和提供的强大智能的功能,由于理念与设计过于超前,早期有一些业内质疑的声音。但由于产品独到的优秀特点和巨大的潜在价值,诸多伯乐开始试用,给了产品在实际场景中不断优化、强化的宝贵机会。经过不同领域、不同客户的各种高复杂度大型系统的百般锤炼、验证后,自2016年开始,精准测试开始赢得全行业的重视和响应。优秀的商用落地性是精准测试技术流行的一个重要因素。由于精准测试把准了黑盒测试无法解决的难题和固有瓶颈,踏准了测试技术发展大的进程,并从设计一开始就特意注重了商业场景下使用要求复杂性,因此它从诞生之日起就不仅仅是一套理论,而是有着非常强的实用性基因和可扩展架构,多场景应用成果卓然。精准测试最底层的核心技术:“一种基于用例与源码双向追溯的测试装置及方法”,类似在重建量子纠缠的场景和数据。它成功的将功能用例和对应的代码二者之间的追溯路线,实现了精准无误的可视化。这彻底解决了在黑盒的状态下,软件工程师们无法获知程序内部运行轨迹的难题。自此开始,计算机处理海量测试数据的强大分析能力开始展现。我们知道,所有的黑盒测试都是人工进行的,它依赖的是人的算力,而软件正确性验证的工作量,主要是组合路径膨胀的问题,人的算力是远远跟不上的。那么精准测试如何从测试的角度对业务进行深度的测试辅助分析呢?我们用量子纠缠的形象类比来说明这个问题。量子纠缠理论我们可以简单理解为两个处在纠缠态的粒子一旦分开,不论分开多远,如果对其中一个粒子作用,另一个粒子会立即发生变化,且是瞬时变化。“一个粒子”会与“另一个粒子”产生相互作用,两个表面互相不关联的粒子,互相作用互相影响,他们对外是一个整体,无法单独描述“单个粒子”的性质,只能描述这“两个粒子”的整体性质,这就叫做“量子纠缠”。类比到软件测试上,我们的被测试软件中常见的软件界面和功能输入输出以及渲染它的复杂代码,他们之间就和两个纠缠在一起的粒子一样,具备强关联性。一个发生了变化,另一个必定发生变化。精准测试的另一核心功能是:“回归测试用例的自动选取”,就是基于用例和代码的追溯(量子纠缠)关系在进行运算之后全自动得出的。用例和代码精准的追溯机制,使数据开始可以实施大量的智能测试算法。在软件测试理论界,大量论文算法的基础都是默认建立在用例和代码的关系上进行分析的,但因其追溯机制工业实现难度极大,所以一直停留在学术理论阶段。星云测试引领的精准测试方法体系,落地在精确便捷的商用产品和大型解决方案中,让大量先进的测试分析方法和理论,得以实实在在的验证。精准测试首次明确的提出了:如何采集和应用双向追溯数据,进行测试辅助分析系统的构建。不管是纯软件系统还是运行在设备中的软件系统,用例与代码精准的数据可追溯性,都是基础性的强需求。比如金融转账业务、用手机拍照、机器人控制器的动作控制指令等,每个操作都有与之对应的代码。星云精准测试产品的强大之处在于:所有的分析算法不需要和被测系统的业务功能做特定的适配,因此可以应用于任意软件系统的测试辅助分析。而传统的白盒在产品实现上以采集和分析总体覆盖率为主,没有能力将覆盖细化到用例层级,因此传统的白盒测试囿于覆盖率的概念中,无法实现更高层次的测试辅助分析需求。星云测试产品的技术优越性及应用的易用性,使它成为新一代软件测试技术流的中坚力量。星云精准测试在企业应用场景中,为了方便客户还设计了完全静默的“傻瓜”运行模式,客户基本无需增加额外的学习成本。比如测试工程师打开测试用例的excel表格,当他准备执行某个用例的时候,只需点击这一项测试用例。随后通过VBA技术,直接调用星云精准测试的后台接口,再附加一整套深入应用后台执行线程的用户标签技术,就可以将用例和代码关联和分离出来(这里说的分离是指类似于J2EE服务端后台应用)。在对外提供多用户并发访问的情况下,可分离出每个用户执行的用例所对应的相应代码。前面我们提到功能和代码这两个量子数据的重建,以前主要出现在研发/开发人员相对比较零散的执行单步调试功能的时候(即单元测试),无法存储。国外白盒工具因其设计基因的限制,无法支撑超大型、高复杂度的系统级测试需求。在星云精准测试体系中,只要测试工程师执行测试用例,几乎不需要额外付出,在建立测试用例与数据的对应关系的同时,瞬间即可将海量数据实时采集并存储起来,用于后续测试大数据的分析和运算上。它的内核设计,使之可以轻松应用在数亿行的超大型应用上,实时采集、存储测试数据,而对原有系统的响应性能不产生干扰。星云精准测试对软件测试体系是重大的技术革新,它从以下几个层面改变并提升了测试:对软件测试的深度进行了大幅度的拓宽,让计算机有能力直接对测试用例进行辅助分析,给出更深入的测试决策分析依据。传统测试主要通过人工业务测试发现缺陷,定位缺陷必须由开发人员进行,二者信息没有精确的数据交互。精准测试主要基于测试人员标识的用例状态,以及自动记录的用例对应的代码路径进行频谱分析。它可以自动计算缺陷产生的代码位置,在发现缺陷的同时,同步产生缺陷定位的结果数据。精准测试提供的测试用例的聚类分析,是基于测试用例的路径空间距离进行聚类,聚类的结果可以直接对用例执行正确性进行审核,对用例进行等价类划分、分析缺陷密集区以及对用例执行分布进行评估等。精准测试提供的回归用例选取结果,经由海量的计算精准推荐而来,总体算法思路与人工分析的思路是类似的,采用人工智能算法模式把大量的计算完全交由计算机去总结分析,精准、稳定、可靠、高速。这个功能相当于解放了测试和开发团队大量的人力资源,彻底解决因团队成员变更而引入的未知风险。精准测试开辟的测试方法新体系,支撑了黑盒测试快速提升测试效能比的刚需。精准测试对于企业来讲,相对于比需要人工编写、维护庞大自动化脚本的自动化测试,工作量是低很多的。精准测试和自动化测试是并行的技术方向,企业应用可以根据各自团队的特点来选择,并没有绝对的应用时序依赖关系。自动化测试近些年的应用遇到一个广泛的批评就是:让测试团队疲于应付用例的编写而不是测试用例设计等测试核心技术。精准测试属于测试分析系统,着眼点和立意相对较高,在测试理论、方法和商业应用落地验证与成果上,形成较好的整体闭环。它把测试需要关注的核心,引领到一个正确方向上来。精准测试在技术特性上解决了测试结果可信性的问题。在精准测试之前,我们所见到的所有测试数据都是易伪造、易篡改的。例如我们通常使用的测试管理系统,一个用例是否被有效执行,绝大部分是在测试管理系统中被人工录入的。精准测试是在用例动态执行过程中,由计算机内部算法真实记录对应的程序运行逻辑,然后形成后面对测试数据进行精确分析的数据源基础。所以其底层程序运行的数据是没有办法也绝对不允许进行伪造和篡改的。精准测试果断去除了阻碍测试行业发展的重要障碍,让测试的结果精准化、可量化、可衡量化、可信化。此举,将大幅减低测试行业本身的管理成本,有效推动测试行业的快速、健康发展。精准测试大幅提升了开发和测试部门的协作效能。前面提到全景调试器,测试工程师执行完用例,即时产生代码层级的全景调试数据;测试完成后一旦用例存在缺陷,那么基于这个全景调试图以及高度可视化的数据路径追溯,开发人员可以直接对缺陷进行定位,不需要在开发环境下重现缺陷,再单步调试以解决问题。实施精准测试之前,开发人员苦于很难有效参与对测试用例进行审核;实施精准测试后,用例的执行结果都映射到了代码层,开发人员通过代码的覆盖视图很容易就可以协助测试人员补充和确认用例,开发和测试的协作变得非常简单和直接有效。 不可否认,在精准测试之前,开发和测试存在沟通不畅的问题。通过精准测试在项目中的有效实施我们会发现,精准测试可以帮助开发提供非常有价值的数据: 例如代码和用例的反向追溯可以帮助开发人员修改代码做参考,精准测试自动计算回归范围减轻了开发人员必须协助测试进行分析的工作量,因此,精准测试对开发和测试来讲,是一举两得、相得益彰的。星云精准测试发明了多个全新的、适应敏捷迭代开发模型下的覆盖率计算方法。覆盖率是白盒测试最基本的技术,但是随着敏捷迭代的开发模式,即使最高端的白盒测试工具已经几乎没有用武之地,原因就在于由于迭代很快,在某个版本上通常只能采集少量的覆盖率后新版本就发布了。本应在一个版本上分析的覆盖数据,分布到了数个代码结构不一样的程序版本上,因此原有的统计方法和参考意义基本上都失效了。精准测试提供了累计覆盖率,能够将多个版本的覆盖率以最新版本的代码结构进行投影,在控制流上进行累加。这样测试团队无需关注每个版本的覆盖情况,即可关注一个完整测试周期内所有版本的累积覆盖率。另外,考虑到敏捷迭代每个版本的测试需求,即:针对特定模块和功能范围进行测试(并不是全量测试),精准测试提供了相关覆盖率的计算方法。它可以自动圈定某个用例和用例集有关的代码范围(这些也是基于用例和代码的关系分析基础上动态计算的),在运行少量用例或者某个功能模块的情况下,它可以自动确定相关代码,把不相关的代码从覆盖率的分母中过滤掉。这样相关覆盖率在仅仅运行部分少量用例的情况下,依然具有很强的统计和参照意义。第三章 精准测试的定义精准测试定义:由中国团队发起并原创完成测试理念设计和商用产品研发的全新测试技术。旨在测试用例执行过程中,全自动建立任意运行模式的软件系统的功能点与源代码之间高度的可视化追溯机制,获取功能点相关的代码覆盖率并进行精准回归等测试用例深度辅助分析算法的应用。精准测试是测试理论界首次同时使用测试用例及其相关代码两个关键因子,进行质量综合考量和分析的创新测试方法体系。3.1 基本定义星云精准测试在测试用例执行过程中,可以同步全自动建立任意运行模式的软件系统功能点与源代码之间的高度可视化追溯机制,即通过内部算法能够将缺陷对应的代码出错位置直接定位出来;能够获取功能点相关的代码覆盖率并进行精准回归等,使测试用例深度辅助分析算法得以强化应用。这是软件测试首次同时使用测试用例及其相关代码两个关键因子,进行质量综合考量和分析的创新测试理论方法体系。这种有效的数据追溯与“量子联动”的突破性技术特性,大大增强了测试的深度与广度,打破了测试部门的成长天花板,为测试过程本身的价值挖掘和测试数据资产的增值,提供了必要而充分的条件。3.2 关键技术精准测试体系贯穿整个软件测试生命周期。精准测试主要侧重于系统级测试,在单元测试、集成测试、系统测试、验收测试、回归测试中,均能赋予测试数据强大的追溯能力。关键核心技术有:测试用例和代码逻辑的双向追溯,测试示波器、覆盖率可视化、回归测试用例的自动选取、缺陷最后执行时序分析、智能缺陷定位、敏捷环境下多版本白盒测试数据的聚合、聚类分析、结合代码结构与动态数据的测试漏洞检出等。精准测试在测试用例执行过程中,为用户从底层自动建立任意运行模式的软件系统功能点与源代码之间的可视化追溯机制,使用户获取用例级的代码覆盖率等多种高级测试数据。这一突破性的创新智能算法,有力的打破了软件开发、测试、维护及管理人员等之间的数据孤岛状态,实现了软件测试过程和结果的高度精准可视化,在软件高可靠性和高可信度方面,提供了全面而有力的数据支撑。精准测试运行模式是灰盒模式,即:可以在黑盒功能测试的时候,同步完成数据采集和分析计算,让“点测”团队也可轻易切入到精准测试模式,无需改变现有测试形式与流程,不增加额外的技术学习与转换成本。3.3 应用范围精准测试适用于任何形态的软件系统。星云精准测试可应用于各种软件包括嵌入式系统、分布式系统、web应用系统、单机软件系统等各类软件的测试,并且完全不受限于被测试软件本身的业务需求。3.4 用户收益实现从发现缺陷到预防缺陷的转型。减少因为缺陷而对整个研发运维流程造成的返工成本。使发现缺陷前移,减少因项目后期的严重缺陷而导致产品交付延期、成本增高,影响交付质量。建立跨需求、开发、测试等部门的测试协同平台。实现业务数据可追溯化和路径可视化。优化技术与业务方案。根据不同的质量要求,优先级顺序、技术实现手段等,实现智能灵活的技术方案推荐、交付和应变机制。减少无数据支撑造成的内耗。企业“精准众测”平台,支撑千人团队同时在线。测试数据可以根据管理需要做分析,为企业内的成本管控提供选型参考。实现跨地区,跨部门的业务人员,开发人员和测试人员的测试协同,即使不在同一办公地点的人员,也可针对测试需求,测试任务,测试用例和测试缺陷等方面进行远程沟通和实时协同作用,最终完成整个测试过程的实施。支持敏捷开发与测试模式。关注新功能的增量测试,支持自动化回归测试,实现高效的IT交付。第四章 精准测试的基础架构介绍4.1 精准测试的技术架构精准测试从某些层面来讲,赋予了测试用例真正的生命力。测试用例是测试数据的一种,当测试数据实现路线可追溯可视化的时候,一些高级算法的强大能力就可以显示出来。我们从精准测试的整体架构图中可以做一具体分析。图4.1-1 精准测试的总体架构图第一层:利用先进的前置编译器,为客户做源码静态结构分析(在客户的实际环境中,根据客户的需求进行相关的技术配合);第二层:将处理好的系统程序放入测试环境运行,测试工程师通过人工或程序自动化的形式,开始执行用例(人工执行用例可以和测试管理平台或者Excel表格方式进行对接),精准测试的 “软件示波器”采集运行数据并进行高速智能运算,获取精确的测试数据;第三层:根据采集的代码与对应的测试用例,在星云精准测试平台中实现用例与源码的互相追溯;第四层:通过精准测试的分析平台,可以对测试数据进行缺陷定位、用例聚类分析、回归测试用例和最小测试用例集等功能的计算,用户还可以根据需求,批量生成相应的测试报告,或进行测试数据高级分析。图4.1-2 精准测试的用例魔方在总体业务架构图的左上角区域,我们可以看到“用例魔方”几个字。大家可能会比较好奇“用例魔方”的涵义,它是精准测试中非常核心的高级回归功能之一。所谓“魔”是精准测试核心算法所赋予的超能力,所谓“方”实际上是代表测试用例的集合,每个测试用例用一个小方块标识,所有测试用例的集合用一个大方块。当我们把精准测试的和用例分析相关的功能画成架构图形表示的时候,它自然而然地看起来就像魔方。精准测试体系中,测试用例对应的代码逻辑精确而完整的实现了全自动化的追溯和存储,因此赋予了测试用例深入分析的基础能力。在精准测试的用例魔方中,目前存在三个面(随着后续功能的增加,将增加分析的面):即回归测试用例选取、测试用例聚类分析、测试用例最小化,同时辅之以智能缺陷定位技术。当测试用例与代码的追溯关系建立的时候,测试魔方的核心功能区即同步构建出来。为数据的多角度分析,提供了丰富的资源素材库。4.2 软件示波器软件示波器是星云精准测试独有的功能,它如同软件的质量运行情况“追溯穿行器”一样,在测试工程师按下启动键的同时,即时开始建立用例与代码的自动关联。示波器把采集到的测试数据通过可视化的窗口界面进行实时展示,内容涵盖采集到的块、条件和函数信息。蓝色波形代表写入的数值,黄色波形表示读取的数值,用户可以清晰的看到被测系统的数据变化。它如同心跳监控仪一样,如果被测试程序发生了崩溃,软件示波器就像人的心脏停止跳动一样,一根横线拉直向用户报警。如果正常采集到数据,会有持续的波形展示出来,高效而精准地监控程序细微的运行状况。示波器精密捕获每个软件单元任何微小的运行波动和行为改变,支持多次运行数据的比对。它可以根据需要记录崩溃前的至少50个块,使“崩溃重现”变得轻松简单。软件示波器中的测试用例可以从现有的测试管理系统导入进来,先选中用例点击开始,驱动被测试系统运行后,软件示波器就会采集到程序内部运行逻辑对应的波形信息。用例执行结束后,点击停止。这个用例运行阶段的数据,通过开始和结束的点击,边界就记录下来了。图4.2-1 软件示波器面板的正下方可以展示函数的各种调用信息。包括类、函数,参数类型等。清晰的列示出参数列表、类运行情况、内存检测数据、数据库拦截等多角度的数据分析追溯情况信息。示波器观测的维度较多,用例与代码的追溯是精准测试的基础功能,后面的高级算法都在这个基础上展开。用例和代码的追溯就像一个全景的调试器,只要功能由测试人员运行,所有的内部代码执行逻辑瞬间就可以展示出来。通过测试数据的反向追溯分析,开发人员可进行一致性修改,避免修改引入新的缺陷,通过正向追溯结果,开发可对用例的执行进行全面掌握,可用于快速修复缺陷和详细实现确认。同时软件示波器也提供一个辅助的等价类划分的功能,它将一个用例从开始到结束所执行的路径信息终值,完整记录下来。如果两个用例终值不一样,就可以确定为不是等价类。对于很多从功能表面很难界定是否等价类的测试用例,软件示波器可以给出精确结果。因此软件示波器的价值与意义在于:(1)只要测试开始执行,即可以透明方式高速采集功能运行过程中对应程序的运行逻辑。(2)在系统高速运转下采集,可保证对原有应用无干扰,超过1500w/s的采集速率。(3)可采集程序的条件,执行路径,执行参数,内存使用等动态运行数据。为了方便客户在运行项目测试时,一边对被测系统做实时数据监测,一边同步观察到示波器里面数据写入和读取的波形,星云做出了实时数据监测的悬浮窗缩略图,减少了切换带来的工作量。它不影响原有被测系统的界面,以半透明悬浮的方式展示在被测试应用界面的前面。图4.2-2 软件示波器悬浮窗4.3精准测试的双向追溯精准测试的测试用例和代码的双向追溯功能,是精准测试核心技术之一。如同前文的“量子纠缠”,即运行测试用例的同时,精准测试可以通过程序自动的记录并追溯到这个测试用例相应执行的代码。如果测试人员关注某一些代码行,它可以追溯出哪些测试用例在运行过程中运行过这段代码。通过这个技术特性,测试工程师的每个测试用例都可以进行量化分析和统计,提供了开发人员和测试人员之间精准的数字化交流依据, 增加测试和开发的交流效率。双向追溯技术记录了每个测试用例对应的程序内部的执行细节,细致到每个条件、分支、语句块的执行情况。开发人员亦可通过双向追溯的结果去理解程序逻辑,进行软件维护以及进行可一致性的修改。4.3.1 双向追溯技术正向追溯将测试用例和代码执行信息自动关联,可追溯到函数级别及代码块级别;通过正向追溯可直接把BUG定位到故障和缺陷逻辑相应的代码,并提供最后运行的时序数据;通过正向追溯自动记录产生功能对应的详细设计实现,辅助软件解耦和架构分析。图4.3.1-1 双向追溯(正向)-测试用例追溯到代码图4.3.1-2 双向追溯(正向)-测试用例追溯到代码4.3.2 双向追溯技术反向追溯将代码执行、函数、代码块级别和测试用例执行信息自动关联;通过反向追溯可直接观察代码变动所影响的测试范围;协助开发,进行代码修改后影响功能的范围评估;协助测试人员对代码修改部分所影响的测试用例进行评估。图4.3.2-1 双向追溯(反向)-代码追溯到测试用例图4.3.2-2 双向追溯(反向)-代码追溯到测试用例4.3.3 数据追溯技术-追溯测试用例的全景调用精准测试通过正向追溯把测试用例运行的代码执行进行了全景绘制,在全景图中,测试人员可以有效的观察到函数之间的整体的调用与走向,观察出被测模块与上层之间的调用关系。图4.3.3 测试用例运行的代码整体调用第五章 精准测试的核心组件与功能精准测试的核心组件与功能:软件测试示波器、用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析、缺陷定位、测试用例聚类分析、测试用例自动生成系统等,完整的构成了精准测试技术体系。精准测试系统本质是一套强大的计算机开发与测试系统,实现了数据的可视化联动,以及开发辅助分析。它的关键技术赋予了测试用例和代码强大的相互追溯能力,随后衍生实现了很多高级测试功能与算法。精准测试系统将用例深入到代码层分析后,可以大幅改进人工测试所产生的各种问题。接下来将从风险控制、工作协同、敏捷迭代方面详细解析精准测试的核心功能和实际收益。5.1 风险控制5.1.1 七种测试覆盖率星云精准测试提供7种测试覆盖率:分别为:SC0语句块覆盖率、True覆盖率、Both覆盖率、CDC覆盖率、Branch覆盖率、MC/DC覆盖率。用户首先选择分析覆盖率的维度,例如选择了“SCO语句块”维度,那么系统就会将被测试程序的所有语句块结构展示出来,并且用颜色表达覆盖情况,绿色代表覆盖,深蓝色代表未覆盖。同时告知覆盖率的分子和分母都是哪些,非常清晰的展示覆盖率可视化结果。精准测试在前期已经对程序的静态结构进行了深度的分析,因此用户在覆盖率可视化界面,根据选择的维度在代码层面上把需要展示的结构单元都结构化的展示出来。例如图5.1.1-1 七种测试覆盖率中的第二张图 图5.1.1-1 七种测试覆盖率MC/DC覆盖率可视化MC/DC覆盖率,即修正判定条件覆盖,该覆盖率数据 MC/DC是DO-178B/C Level A认证标准中规定的、欧美民用航空器强制要求遵守的覆盖率标准。MC/DC覆盖率可以基本保证被测试软件无缺陷,对于大型系统的可靠性要求很高的一些关键模块,建议采用这个覆盖率标准。MC/DC覆盖简单来说,就是追踪复合条件中每个子条件的真假翻转,在其子条件不变前提下,是否都独立的影响了整个条件的真假值。星云精准测试系统会自动的把各种组合都列好,通过颜色表达哪些子条件已经满足,以及对应满足情况时其他独立子条件的组织情况。图5.1.1-2 MC/DC覆盖率可视化条件组合可视化展示精准测试对于多条件组合的代码,采用了最新研发的条件组合可视化视图进行展示。视图中,用户可以观察到每个条件的真假运行情况,以及条件与条件的组合运行情况。对于测试人员来说,当全部条件组合之间的T与F都完全满足时,即可实现代码全路径覆盖。图5.1.1-3条件组合可视化展示5.1.2 新增代码覆盖率敏捷模式下,因迭代频繁其存量的代码量很大,通常更关注增量覆盖度量。精准测试可以在程序新版本发布后,自动计算新增(变更)代码的范围,给出新增代码的覆盖率。覆盖率的分母中的函数都是变更和新增的函数。与此同时,基于反向追溯的功能,我们还可以给出新增代码对应的测试用例名称。当某个新增函数没有达到很高覆盖率的时候,我们通过反向追溯的用例,可以判定因为哪些功能范围的用例设计不充分,导致了新增代码覆盖率不高。图5.1.2 新增代码覆盖率5.1.3 测试覆盖率范围筛选与再统计在做精准测试或统计覆盖率时,往往测试管理者、开发人员、测试人员为了保证测试覆盖率的正确性,会对某个方法、类进行查看或在统计中把代码中一些废弃的函数、某些特殊情况下无法测试到的代码进行移除(至少是做相应备注),从而让测试代码覆盖统计率达到更加准确。星云精准测试在设计中,通过多种搜索、方法、类、模块过滤等功能,把需要统计的范围进行缩小、不需要统计的去除。根据用户的选择,进行覆盖率再统计展示。图5.1.3 测试覆盖率范围筛选与再统计5.2 工作协同5.2.1 打通开发与测试的隔阂精准测试打通开发与测试的协同工作通道,使得开发与测试能够更好的沟通,提高工作效率。传统模式下,开发人员关注的是代码,测试人员关注的是业务角度的测试用例,彼此的直接关联相对较弱。开发和测试的沟通,基本就是采用自然语言、Excel表格、内部系统等,存在交流信息不够严谨的问题。例如测试工程师发现一个缺陷,提交到缺陷系统,开发需要花费大量时间再行理解、准备数据、复现、调试,直到最后的修正。因为业务上的功能执行和代码并没有明确的关系,通常测试工程师执行完功能测试用例后,让开发人员帮助评审也非常困难。若测试工程师提供的测试结果都是比较模糊的功能逻辑描述,重现缺陷需要花费大量的时间。开发人员修改代码后,对于变更描述,以及变更引起的关联问题描述通常也都很模糊,导致测试又出现新问题。企业采用精准测试技术后,通过执行用例可以直接追溯到对应执行的程序代码块,这样的数据化沟通,将使开发人员和测试人员之间的协同工作效率大大提高。图5.2.1 协同模式5.2.2 源码动静态数据的统一星云精准测试通过插装得到的项目静态结构信息,结合测试后采集到的测试数据,能够精准记录测试的过程,通过这些静态数据和动态数据视图,便于开发人员基于图形化结果进行快速分析。对于不懂开发的测试工程师,通过程序控制流程图的图形以及通过颜色表示的覆盖信息,可以直接看到程序内部漏测的逻辑是什么,也可以通过这些结果直接与开发沟通,进行辅助用例和逻辑的补充。因为内部逻辑的强追溯性并且能够图形化的打开,可以有力保证黑盒测试后期开发快速理解并解决瓶颈问题,保持全程测试的高效执行。图5.2.2-1 源码静态结构与动态测试数据统一图(函数调用图)图5.2.2-2 源码静态结构与动态测试数据统一图(控制流程图)精准测试在程序静态分析的基础上,可以对程序绘制可视化的图形,同时将动态执行的覆盖信息染色到这些视图上。对于不懂开发的测试人员也可以很清晰的看到程序的哪些结构节点没有被覆盖到。例如图形中蓝色的节点是未覆盖的节点,绿色的是覆盖的,因为控制流程图本身有分支,嵌套等各种关系,测试工程师可以很容易判断出覆盖的范围大致是哪些。而如果有开发人员介入,可以更清晰地分析测试工程师执行的用例所遗漏的程序逻辑。图 5.2.2-3源码静态结构与动态测试数据统一视图5.2.3 缺陷最后执行时序分析星云测试可自动捕获缺陷或崩溃发生时,程序最后执行的详细路径信息。缺陷发生后,开发人员能够直接看到缺陷出现时,代码执行的时序和路径信息,直接定位缺陷并排查问题,节省大量的沟通、复现和调试的时间成本。当功能执行发现缺陷后,在软件示波器上可以立即按下“停止”键,那么最后执行的代码序列就可以被抓取到,开发人员可快速定位缺陷最后执行的50个代码块、条件、判断的各种执行信息。在下面视图中,我们可以根据标号(从1到50),看到代码最后的运行时序,在图形里面的每一个绿色小方块为一个代码语句块或者一个条件、判定,在一个大方框下的绿色方块代表一个函数内部的代码块。经过布局算法后产生下述图形。图5.2.3 缺陷最后执行时序分析5.2.4 智能缺陷定位通常测试工程师只是负责发现缺陷,缺陷的具体定位只能交由开发人员来执行。精准测试打破了测试部门的天花板,即通过内部算法能够将缺陷对应的代码出错位置直接定位出来。这相当于增加了测试深度,同时体现了测试数据和测试过程本身的价值。对于测试工程师来说,只要发现用例相似而程序在输出上有对错区分,就可以使用精准测试的智能缺陷定位功能。精准测试平台通过测试人员在功能测试阶段标记的用例执行状态,以及软件示波器自动记录的程序运行频谱,自动分析缺陷的出现的代码块。因精准测试平台可以获取每个用例执行时详细的路径追溯信息,测试工程师只要告知系统用例的状态,例如是否通过是否正确,那么精准测试内部算法就会去根据正确和失败的路径差异进行计算,给出缺陷出现具体位置的可疑度排名。这个功能可以大大增强测试在整个开发流程的参与度和测试数据价值,也让开发人员减少了自己去模拟场景的时间,快速提高开发和测试的协同和配合的效率。对于同类测试用例,经过多组测试可给出非常有效的结果。列出的可疑代码,可直接通过测试过程给出,提升测试的价值及产出。图5.2.4-1 通过功能测试频谱法分析进行智能缺陷定位选择可疑度算法、得到可疑度高的代码块,关联源码后,可根据代码可视化查看具体位置。可疑度计算有一个公式,并不复杂,通常每个代码块有2个变量,四种状态值。分别是:是否执行、是否通过,这样每代码块都有一个可疑度值。星云精准测试提供3种常用计算公式,供大家参考。aep表示通过且覆盖到该块的测试用例的个数、anp表示通过且未覆盖到该块的测试用例的个数、aef表示未通过且覆盖到该块的测试用例的个数、anf表示未通过且覆盖到该块的测试用例的个数。结果表示该块的可疑度。图5.2.4-2 智能缺陷定位展示5.3 精准测试对敏捷迭代的支持5.3.1 敏捷迭代下多版本白盒测试数据的聚合在敏捷环境下由于版本迭代速度很快,每个不用的代码版本上通常只能采集到少量的覆盖率。一旦发布新版本,就意味着代码发生了变化,覆盖率数据就需要重新采集,但是每个版本采集的少量覆盖率,从分析层面上并没有多大的意义。针对以上问题,精准测试给出了“**累计覆盖率”**的计算方法。它将一系列迭代版本的覆盖率,在最新的程序版本上进行投影累加。用户可将一个阶段各个版本的覆盖率累加起来进行分析。算法的思路是以最新版本代码为基础,以某个函数为单位,一直往前累加。直到这个函数在之前的某个版本代码发生了变化,就停止累加。例如下图函数A的4个版本的覆盖都可以累加,是因为这个函数在4个版本中都没有发生变化。而红色的函数B只累加了3个版本,是因为从版本v2.0-2后这个函数发生了代码变更。之前的代码覆盖就不能累加了。星云精准测试-敏捷环境下多版本白盒测试数据的聚合如图所示。图5.3.1-1敏捷环境下多版本白盒测试数据的聚合这种累加是在确保函数代码没有发生变化的情况下,在控制流上对相应节点的覆盖率进行累加。例如下图中3个版本,覆盖率不同的分支和控制流,数据累加后可以看到所有分支都覆盖到了。图5.3.1-2敏捷环境下多版本白盒测试数据的合并分析5.3.2 聚类分析星云精准测试提供的聚类分析功能,根据测试用例的函数执行剖面的向量化信息,对测试用例进行精确的空间距离计算后执行聚类分析。聚类结果可以分析被错误执行的用例,例如不相关的功能点聚类到一起,则说明其测试执行可能存在错误。精准测试提供的聚类分析功能,也可以辅助找到缺陷分布的密集区域。大部分情况下,缺陷分布会呈现2/8的聚集特性。在时间紧张的情况下,我们可以通过聚类结果,每个类选取中心点以及周边几个用例。如果没有问题,就可以去测试其他聚类,如果发现一个类缺陷概率高,那么这个类就需要进行重点测试。通过聚类结果可以分析测试用例的分布密度等信息,辅助进行测试决策。图5.3.2-1 测试密度下图中测试用例分类都有一个名字,这个名字是聚类结果中每个类中心点的测试用例名字,它基本标定了这个类的功能点范围。聚类的大小代表了某个功能范围测试是否足够充分。例如一些应该重点测试的功能,聚类结果中的用例数应该很多;一些小众的功能,聚类中用例数应该比较少。一个类中用例数越多,这个类圆圈比例越大,反之则越小。在聚类的基础上,我们也可以对一个类中的等价类用例进行分析。是等价类的用例,会分成一组专门展示。 图5.3.2-2 聚类分析5.3.3 覆盖率漏洞检出在敏捷迭代过程中,通常没有充分的时间将所有函数的覆盖率都达到一个很高的层级(Level)。精准测试结合代码结构和动态数据综合分析,通过计算直接筛选出潜在的高危测试漏洞,可以在短期内确定高危漏测模块并针对性的解决,帮助用户快速找到严重缺陷。当测试时间不充分的时候,执行完黑盒测试以后,先看测试漏洞列表,里面显示了通过静态信息和动态信息计算,得到的最高风险的漏测点模块。我们可以通过复杂度进行计算,因为复杂度高的模块一般来讲,它是相对重要的模块并且逻辑复杂,如果动态覆盖比较低,我们会优先筛选出来进行排序。处于调用和被调用中间的模块,因为属于中间关键模块,我们也会计算它的扇入扇出,和动态覆盖率信息。如果它的比率很高,将被认为是高风险模块,被筛选出来进行排序。回归时,应优先测试风险指数高的高危模块,补充他们的覆盖率。图5.3.3漏洞检测列表5.3.4 最小测试用例集精准测试也可以对用例集进行优化。比如用户有大量用例的情况下,尤其是自动化用例集含有长期维护的冗余用例。精准测试平台可以对很多重复用例的逻辑进行筛选和过滤,优化出满足当前总体覆盖的最小用例集。图5.3.4星云测试最小测试用例集第六章 精准测试支持不同剖面的分析报告在时间有限,经费有限,资源有限的情况下,我们既要考虑测试是否充分,也要顾及时间、人员、设备配置等限制条件。精准测试可以支持企业不同剖面的软件质量分析需求。6.1 详细的测试总结报告内容测试资源分析:多少人,多长时间、整体测试有效率及执行率测试结果分析:描述需求的测试结果,系统实现了哪些功能点,哪些还没有实现缺陷情况分析:缺陷复现、缺陷处理、缺陷数量、属性、状态分布,缺陷预防、缺陷收敛度度量指标分析:测试覆盖率、测试执行率、测试执行通过率、测试缺陷解决率效率指标分析:进度偏离度、缺陷发现率、用例执行效率和质量等高风险识别与排序:注明当前项目中面临的最严重、最优先的问题整体评估:哪些功能已经实现,哪些功能还未实现,还遗留哪些问题,遗留缺陷分析优化建议:测试过程优化,从测试组的角度为测试工作提出建议图6.1 星云测试的差异报告分析6.2 高级回归测试报告对前期测试执行阶段发现的问题、缺陷集中的功能,业务比较重要且使用频繁的功能进行再次测试,确保系统上线后,已被修复的问题不会重新出现,重要的、高优先级的业务不会发生错误。图6.2智能回归测试用例选取6.3 测试用例库评估报告软件测试的主要工作,是把软件需求映射为软件测试。测试用例是软件测试全过程的核心,也是测试执行环节的基本依据。精准测试充分满足软件测试执行过程中的各种指标:测试用例执行进度测试用例通过率测试用例颗粒度分析识别无用的测试用例识别冗余的测试用例从侧面提供增添新测试用例的依据从侧面提供调整测试用例库结构的依据图6.3 测试用例最小集6.4 精准测试的VIP企业内网私有云可信化报表星云精准测试提供多个剖面的高可靠性测试质量进度追踪报表。当客户端录入测试用例并采集数据后,用户内网web端将产生实时、详实的测试数据分析报表。该报表与普通的测试管理系统不同:普通的测试管理系统人为录入数据的情况比较多,使得数据状态的真实性没办法确切保证。精准测试提供的报表,底层数据来自于执行测试用例时候代码数据的采集,通过专用底层接口上传,完全无法进行数据调整或者篡改和伪造。通过浏览器登录测试系统,选择需要跟踪的项目,就可以实时对整个测试的质量、进度、人员进行精准的分析和管理。企业内网云端管理系统展示的数据基于精准测试数据的分析,所有数据原生精确,支持移动测试+本地测试。测试团队、开发团队、甲方负责人等多种角色都可以登录系统,从各个层面对测试、软件质量进行分析。图6.4项目汇总展示6.4.1 精准测试的VIP企业内网私有云-测试效率的直观展示精准测试报告可直观分析每天的测试效率,通过代码模块和复杂度关系图,看到函数群落测试情况分布及趋势,可直观精准识别系统测试所处阶段。每日增长覆盖率报表:管理者可以清晰的看到整个团队的效率趋势变化,比如刚开始测试的时候覆盖率增长快,到了黑盒测试瓶颈点上升就很慢了。这时候精准测试技术就开始发力,可以清晰地看到它在弥补效率损失方面的优势。覆盖率和复杂度报表:可以很直观地看到测试的质量深度,例如在测试不充分的时候,复杂度高的模块通常覆盖率都比较低,统计点分布自一个左上角的区域(表示高复杂度+低覆盖率),而当测试深入进行,这些点就会向右侧移动。管理者可以非常直观的看到系统测试的充分程度和上线的质量把握。图6.4.1-1 覆盖率每日增长趋势图与黑盒测试瓶颈图6.4.1-2 测试效率换档点与测试深度趋势观察表6.4.2 精准测试的VIP企业内网私有云-测试用例排行图测试用例排行分析报表:可直观展示参与测试工程师所执行的用例数、通过率和缺陷率,真实记录并分析每个测试用例的实效性。星云精准测试-测试工程师实效精准分析系统,将参与的测试工程师所执行的用例从逻辑覆盖映射到代码覆盖,真实记录并分析每个测试参与者的工作实效。以逻辑覆盖为基准的而不是用例数量为考核标准。图6.4.2 测试用例排行图6.4.3 精准测试的VIP企业私有云-测试用例双向追溯与覆盖率可视化通过用户的内网精准测试web报表,可以追踪每个测试用例执行的覆盖率和执行的函数路径信息,那些没有真正执行的用例,将无法伪造其对应的覆盖率信息。图6.4.3-1测试用例双向追溯追踪每个用例执行的函数信息以及具体的代码覆盖信息,在web端展示代码覆盖率视图,更具体的分析用例的执行情况。图6.4.3-1覆盖率可视化6.5 精准测试在数字化转型中的作用 完成数字化转型。提高软件交付质效、实现快速迭代持续交付,有效呈现测试价值。培养业务和测试的“两栖专家”。从不同角度提供有价值的数据依据,使测试团队既能熟悉业务知识、业务场景,又具备较强的业务分析评估和整合创新的能力。数据化交付。实现企业内部云平台建设、明确各工程活动环节的交付物和交付标准,并将质量验证标准、验证手段和监控工具嵌入流水线,保证各环节的有效性,对质量趋势进行提示和预警,及早发现缺陷,实现质量可视、过程可追溯、可审计。建立测试数据资源池,整合测试资产。利用大数据、人工智能等技术建立质量智能分析模型等,为“产品质量智能分析平台”提供有效数据。减少因人员变动而产生的成本影响。一般外包人员的流失率普遍为20-40%左右,重新招聘和培养,将对项目进度及成本进度,造成很大影响。因篇幅限制,完整版可查看 星云测试官网(www.teststars.cc)
-
不求甚解亦或是固步自封,都是从事IT行业所不可取的。如果只会写一手好代码,却不会思考,那只能称作码农,而不是Coder。写了这么多年的代码,做了那么多年的开发项目,你是否曾经有过这样的迷茫和困惑——技术发展日新月异,奋力追赶的我们,究竟在缔造着什么?程序员的宿命?程序员的职业生涯中难免遇到烂项目,有些项目是你加入时已经烂了,有些是自己从头开始亲手做成了烂项目,有些是从里到外的烂,有些是表面光鲜等你深入进去发现是个“焦油坑”,有些是此时还没烂但是已经出现问题征兆走在了腐烂的路上。国内基本上是这样,从英文社区和技术媒体上老外同行的抱怨程度看,国外应该是差不多的,虽然整体素质可能更高,但是也因更久的信息化而积累了更多问题。毕竟“焦油坑”这些舶来的术语不是无缘无故被发明出来的。Any way,很多人认为这大概就是这个行业的宿命——要么改行,要么就是与烂项目烂代码长相伴。面对这宿命的阴影,有些人认命了麻木了,逐渐对这个行业失去热情。那些不认命的选择与之抗争,也基本是一股脑的乱撞,去做出种种判断和尝试。精通那么多技术为何还是做不好一个项目?其实,软件开发人员的工作职责远远超过单纯的计算机编程。在参与软件开发的整个生命周期中需要开发人员担当多个角色,努力通过研究和替代技术等解决问题的方法来实现产品研发目标,从而改进整个产品。程序员的迷茫和困惑不仅仅是面对技术繁杂的无力感,更重要的是因为长期埋没于软件世界的浩大的分工体系中,无法看清从业务到软件架构的价值链条,无法清楚定位自己在分工体系的位置,处理不好自身与技术、业务的关系所致。组件臃肿:Service 组件的个数跟领域实体对象个数基本相当,必然造成个别Service 组件变得非常臃肿——API 非常多,代码行数达到几千行;职责模糊:业务逻辑往往跨多个领域实体,无论放在哪个 Service 都不合适,同样的,要找一个功能的实现逻辑也无法确定在哪个 Service 中;代码重复 or 逻辑纠缠的两难选择:当遇到一个业务逻辑,其中的某个环节在另一个业务逻辑 API 中已经实现,这时如果不想忍受重复实现和代码,就只能去调用那个 API。但这样就造成了业务逻辑组件之间的耦合与依赖,这种耦合与依赖很快会扩散——新的 API 又会被其它业务逻辑依赖,最终形成蜘蛛网一样的复杂依赖甚至循环依赖;接下来,让我们从项目管理聚焦到项目代码这个相对小的领域来深入剖析。开启程序员职场破冰之旅——云开发平台大有可为要想成为软件开发的专家,需要完整了解软件开发的流程,并在关键部分掌握丰富经验。需要了解设计模式同时遵循软件开发的最佳实践,包括创造性和思考力。传统上实现这一目标需要掌握代码管理、服务器端开发、客户端开发、运维、云计算、网页设计、分布式系统、数据库、编程规约、基础设施管理、可扩展性、安全性待方面的能力。而作为新潮开发工具的云开发平台将底层技术和公用应用封装成参数模块,开发人员无需再经过繁琐的编程代码去实现功能需求,只需使用可视化、配置式配置元数据即可开发系统。软件开发作为一种商业活动,判断其成败的依据应该是:能否以可接受的成本、可预期的时间节奏、稳定的质量水平、持续交付满足业务需要的功能市场需要的产品。其实就是项目管理四要素——成本、进度、范围、质量,传统项目管理理论认为这四要素彼此制约难以兼得,项目管理的艺术在于四要素的平衡取舍。而云开发平台的综合性及简便性,在保持一个质量水平的前提下,成本、进度、范围三要素确确实实得到了改善,更不必牺牲某一方面比如牺牲成本(加班加点)来加快进度交付急需的功能。让技术开发者可以更专注于软件项目的设计及管理,从而提高软件开发效率及项目质量,轻松成为全栈软件开发工程师!
-
因为联盟盟主每月都会上报联盟数据,月浏览量、月加入人数什么的。如果每次手动统计的话就非常不方便,所以我尝试着写出一个工具来采集数据自动统计一下,以求代替繁琐的人工操作。目前计划版本:已完成: 1.0:帖子数据采集和联盟数据采集、可视字符界面表格。 2.0:优化采集过程,完全函数化。可生产html的表格。计划中: 3.0:可按日或周统计数据,产生相关报表。 4.0:云服务化{在网页操作,不用下载客户端}采集效果: 生成的网页: 字符界面:也许你发现了,这个联盟id是什么?我定义的联盟id是联盟url后的数字,比如下面链接中的1111就是联盟id。https://developer.huaweicloud.com/hero/group-1111.html而且这个id好像是注册排号的,也就是说后期有希望搞个联盟大数据。都爬进来代码完全开源欢迎各位体验和反馈。开发日志:https://bbs.huaweicloud.com/blogs/192345开源地址:https://gitee.com/1091198228/huaweilianmengshujucaiji我是新入行小白,对代码结构不规范的地方还请大佬们指出,我一定快速改正。
-
作为敏捷开发中测试团队的一员,在微服务测试过程中,你是不是也遇到同样困惑:服务不具备独立验证能力、自动化用例开发效率很低等?华为云DevCloud API全场景测试技术来支招~围绕API的全场景,打造6大测试服务为微服务的上线质量护航,快来看看吧~https://www.huaweicloud.com/product/cloudtest.html
-
背景在一次DevOps线上活动的提问环节中,有这样一个问题:“我们公司刚刚完成了DevOps转型,搭建了一条流水线,流水线确实让我们部署上线的效率提升了,但是也更快的让客户当上小白鼠,因为我们让问题更快的暴露出来了……”可以想象,一个开发人员开心的点了一下流水线的启动按钮,然后就开心的下班了,然后用户看着屏幕的404,然后就没有了然后(坏笑)……其实这真不是开玩笑,如果将项目中的流水线发布权限下放给了开发,那么404真的就是很现实和普遍的问题,因为很多开发认为自己的工作就是开发。像这样的坑你是否也掉进去过,我们一起基于此,来看看什么样的流水线是不坑人的吧。问题分析这些年来,DevOps已经逐渐的深入到软件企业中,尤其是一些互联网的项目中,需要更快的适应市场的变化迎合用户的需求,传统低效的方式来部署生产环境已无法存活, DevOps的流水线(部署流水线)也应运而生。然而在企业追求高效的同时,往往又引入了新的问题——高效的将bug展现给了用户。项目开发的前期,代码都是比较简单的,开发团队的人员也比较少。但慢慢随着时间的推移,代码越来越复杂,团队成员越来越越多,经手变更的也越来越频繁。一旦出现了问题,作为一个开发人员往往首先会想到的是——快速修复上线。DevOps在速度这点上确实帮了大忙,经历过传统部署发布的同学肯定深有体会。但是这样往往却又伴随了新的问题——“打地鼠现象”。何为打地鼠现象? 简单的说,就是一个问题你修复了,可能又会蹦出来几个新的问题。长期下去,不仅开发团队压力大,客户更是成了“小白鼠”。其实,这并不能把问题归结到部署流水线身上(无辜躺枪),借助流水线的快速发布,只是将问题更早的暴露出来,其归根结底上,问题还是出在质量上。那么,我们就不得不谈到——质量内建。戴明(William Edwards Deming)曾提出“问题发现得越早,修复的成本越低”,有数据指出85%的缺陷都是在代码编码阶段引入的,然而大部分的缺陷并不是在编码的时候发现的,而是在之后的测试阶段发现的,甚至是已经上线后。而且随着越往后发现缺陷,修复的成本也越高。来源《Applied Software Measurement:Global Analysis of Productivity and Quality》按照STICKYMINDS网站上上的一篇名为The Shift-Left Approach to Software Testing的文章中所给出的(如上图),假如在编码阶段发现的缺陷只需要1分钟就能解决,那么单元测试阶段需要4分钟,功能测试阶段需要10分钟,系统测试阶段需要40分钟,而到了上线之后再发现可能就需要640分钟来修复,这可以说是很难让人接受的,所以质量内建是至关重要的。在质量问题上,当然离不了我们老生常谈的开发阶段的编码规范、重构、检视等活动,这里不做叙述。随着DevOps的引入,我们需要将质量内建,加入到DevOps的各个环节中,而部署流水线就是贯穿这些环节的重要工具。某种程度上说,部署流水线的质量基本上决定了软件质量——是带伤上阵还是安稳的睡大觉,部署流水线是关键。那么如何算是一条不坑的部署流水线呢?解决方案测试左移(Shift-Left testing)如上面所提到了,在开发完成后,越到生命周期的后面修复的成本越高。那么基于这样的情况,测试应该尽早的开始。在传统的开发周期中,问题都在什么时候发现的呢?如下图所示:来源《Applied Software Measurement:Global Analysis of Productivity and Quality》可以看到传统模式下,问题很少会在开发阶段发现,而现在提出的测试左移,就是在要在开发阶段尽可能的发现更多的问题,而避免问题被发现在之后的阶段。这也就是我们搭建一条不坑的流水线最基本的理念之一。一般来说,在流水线的构建阶段我们会加入静态代码检查,比如使用Findbugs、Sonar等。可按需自行设置是否随代码提交而触发检查(推荐),或伴随持续集成的工程实践开展,可一天一次或多次,这就保障了不会掉进最基本静态代码层面的坑。此外在流水线的设计上一定要有API、UI等自动化测试。一般来说,可按部署到不同的环境,对应创建不同的流水线阶段,如集成环境有对应的流水线的集成阶段,测试环境有其测试阶段。当部署时,就可以在对应的阶段中加入所需要的测试活动。如当开发人员修复某一个bug后,想要保证其他功能的成长,可以通过在流水线的自测阶段通过加入API的测试等。总结来说,就是在流水线的构建阶段加入静态代码的检查,在部署阶段加入自动化的测试活动,以保证代码的质量和功能的可用。质量要求在质量建设中,不能仅停留在质量管控的基本要求——有,还要注重质量的高低。因为某种意义上来说,较松的管控等于没有,这也是最坑的地方——有等于没有,试想如果流水线中只有某个API测试的情况,那么验证的基本就是这个服务有没有成功启动而已。那么,这就需要加入一个质量阀值的要求。质量阀值的高低是一个衡量质量高低的重要标准。这个阀值可以是接口覆盖率达到多少,也可以是静态代码检查出来问题的数量等。一个严格的质量门禁可以说流水线完成后发布上线的定心丸,这也就可以用来解决了上文提到的“打地鼠”现象。一般来说,接口测试的覆盖率建议达到百分之百,而考虑单元测试和接口测试某些程度上的重复以及UI测试的ROI等因素可按需进行配置,因情况不同这里不做叙述。对于代码检查来说,也可设置某一个数值作为阀值,这个数值可以按照某种规则设定。如一般问题记1分,严重问题记5分,安全问题记8分等,当检查后所累积的数值超过10则不能发布或进入下个一个阶段,当然数值越低越好,具体设置(代码检查的维度不再此叙述)也需按实际情况而定。此外,还可以考虑如开源第三方jar包的扫描、安全漏洞扫描等活动。如果考虑划分的更有层级和模块化,相对于接口测试或静态代码检查的质量建设,扫描的可以单独作为一个阶段按需设置。总结来说,质量建设按照项目的实际情况来设置,而且对于团队来说,质量永远不仅仅是某一个人或团队的事情,而是所有人的事情。相对于质量的坑来说,意识上更是我们应该避免掉进的坑。如了解更多请访问华为DevCloud流水线的内容,详见附录。这里提到了意识,意识是关乎人的主观性层面的了,那么应用在流水线上,其实也是需要考虑的。诚然有些时候我们依赖于机器和自动化,如上面说提到的接口测试、安全扫描等。但是也不能完全依赖于自动化,比如我们也需要人工的代码检视活动。这在搭建流水线的时候也可以考虑到把人工环节加入到其中,比如在发布到生产环境的阶段增加一个发布看板,其中包含了是否有人工代码的检视以及检视出来的代码的质量的阀值或要求等。综上,一条流水线除了必须的、按需的自动化+人工以外,还需要在实践的过程中不断的总结结合自身特点加以定制,然后才能放心大胆的点击“启动”而不被坑。最后引用姚冬老师文章中的一句话“流水线确保代码和基础设施始终处于可部署状态,所有提交到主干的代码都可以安全的部署到生产环境。”这也是笔者非常认同的,也相信搭建实现这一目标,提供这能力的流水线才能更好的实现持续交付,配合好我们的DevOps转型。参考附录测试左移以终为始,再谈持续交付流水线DevCloud的HE2E DevOps的流水线构建博客
-
5月29日华为重庆开发者(HDZ)社区在仙桃国际大数据谷落地成立,有利于仙桃数据谷营造更加丰富多样、开放共享的软件产业生态,聚集更多软件开发人才,打造“中国软件名园”。 市经信委、市软件行业协会及渝北区有关单位负责人和40余家科技企业负责人、研发高管参加。(华为云MVP 颁奖)HDZ是Huawei Developers Zone的英文缩写,是华为开发者生态面向全球开发者建立开放、创新、多元的开发者社区组织。HDZ携手全球开发者,共建开放、创新、多元的开发者社区组织,致力于帮助开发者学习提升、互动交流、挖掘机会,推动ICT、互联网等产业生态的建立和发展,面向云计算、IoT、人工智能、5G、区块链、鲲鹏、昇腾、软件开发与运维、开源等各技术领域感兴趣的开发者、软件工程师、创业者开放。渝北区有关负责人介绍,当前,渝北正抢抓大数据智能化发展新机遇,加快打造软件和信息服务业等五个千亿级产业集群,促进新旧动能接续转换,推动经济高质量发展。华为开发者社区在仙桃数据谷建立,将营造更加丰富多样、开放共享的软件产业生态,有利于凝聚一批软件开发人才。(参会者合影留念)华为与重庆、与渝北区有长期良好的合作,早在2018年8月,华为重庆软件开发云创新中心落地仙桃数据谷,已为420多家中小软件企业提供华为云服务,并深入参与到园区软件产业生态打造中。华为重庆云与计算业务部部长曾玉峰表示,华为开发者社区面向重庆区域在仙桃数据谷率先建立,并将其作为常驻的活动基地,为广大开发者编织好了成长的摇篮,期待大家发挥聪明才智,开发出更多优质的作品,回应社会期待和市场需求。(参会者 留影) 本次活动除了线下现场交流,还采取了线上直播的方式,各方建言献策,使能开发者碰撞出很多智慧的火花。为营造良好的活动气氛,筹备组历时2天拼装了极富创意的开发者乐高展板,吸引参会者纷纷拍照合影。在随后举行的首场技术生态交流会上,华为云生态经理杨明、鲲鹏计算产业生态重庆中心CTO廖洪贵进行了分享交流。相关负责人表示,HDZ重庆社区的首场活动,获得了众多开发者的大力支持,有近百位开发者加入了HDZ重庆社区。重庆HDZ社区向现场开发者及各地观看直播的观众发出邀请,喊话“开发者们,我们在HDZ等你”,希望联合社会各界力量共同打造HDZ开放、创新、多元的开发者社区组织,推动ICT、互联网等领域产业生态发展。HDZ社区—携手全球开发者 共建开放、创新、多元的开发者社区组织 HDZ是Huawei Developers Zone的英文缩写,是华为开发者生态面向全球开发者建立开放、创新、多元的开发者社区组织。 致力于帮助开发者学习提升、互动交流、挖掘机会,推动ICT、互联网等产业生态的建立和发展。 对云计算、IoT、人工智能、5G、区块链、鲲鹏、昇腾、软件开发与运维、开源等各技术领域感兴趣的开发者、软件工程师、创业者、运营人、产品人、大学生、老师等都可以参与到HDZ。 HDZ秉承开放、创新、多元的社区文化,完全由各地HDZ组织者、志愿者自发组建和领导。华为公司不直接参与HDZ组织建设和领导,只按需对HDZ社区活动提供必要的方向指导、资源支持、活动支撑等,并为各地HDZ组织者提供与全国组织者互动交流的机会。
-
作者:Yegor Bugayenko 译者:徐毅本文为《Quality Wall to Protect Developers Against Stress and Fear》文章的内容摘要,1200字带你领略质量墙的魅力,完整译文,敬请期待。前言程序员到底应该为所写软件的质量担负多大的责任?有人认为程序员应该为产品负责,也有人认为程序员的主要责任是交付速度,项目质量是项目要去考虑的问题。 程序员编写软件的过程中,会创造有缺陷代码或“Bug”。软件项目的主要目标之一就是在提升质量的同时减少Bug数量。手工测试和同行评审等常用方法都是等代码里已经出现了Bug才去寻找,过于被动。采取预防措施提升代码质量的代价更低,也更为人所青睐。 “招募更好的程序员”是最为流行的一种方法,我们都认为更专业、更昂贵和更有才干的程序员能够写出没有错误的代码。然而,真相并非如此。正如Kaner等人所言,“程序员相互之间存在着巨大的差异,但没有谁的工作是不会出错的”。 责备那些产出了Bug的程序员们,是另一种同样备受质疑的方法。其负面影响广为人知,弊远大于利,导致程序员们压力越来越大、工作越来越慢、抛出更多代码,被称之为“恐惧驱动开发”。但正如Evans知名博文“恐惧让你成为更糟的程序员”所言,对软件开发来说,恐惧只会让我们事与愿违。 打造“质量墙”所有程序员都会犯错,但他们不应该因此而被责罚。该如何解开迷局呢?该怎么做才能够减少代码缺陷、同时允许程序员随意犯错呢?办法是有的。别为了代码质量责怪他们,让项目去关注质量、让程序员能够无所畏惧地全速编码,效果好得不是一点点。办法就是打造一面强大的、自动化的“质量墙”,守护其代码基。墙越强大,程序员就越觉得安全。首先,他们将在自己的“特性分支”上修改代码和犯错误;其次,向主代码基提出合并代码变更,建议采取拉取请求的方式;第三,质量墙将验证这些变更,如果发现任何新错误就会拒绝合入;最后,只要作者移除掉所有错误,质量墙就会合入这些变更。 如何构建这堵“墙”软件项目可以采取如下一些技术性和组织性的措施来构建这样的质量墙,并保护源代码不被程序员们所破坏。- 自动化构建- 单元测试和集成测试- 强制覆盖率阈值- 变异覆盖率阈值- 强制静态分析- 多步骤代码评审- 只读主干分支 “质量墙”让程序员快速交付,保护项目让程序员在合并前备受折磨的障碍还有很多。Nygard在他的《发布!软件的设计与部署》书中给出了建议。测试失败?拒绝。Lint有告警?拒绝。集成测试导致构建失败?拒绝。换句话说,拒绝变更的动作越快速越便宜,给项目带来的好处也越大。问题是,如果流程和代码仓有这么多限制,一个程序员怎么做到更快速地交付呢?如果质量墙已经罩住整个项目,那么如下这些技巧,不管谁用都能受益:- 提交更小变更- 以退为进- 别害怕搞破坏- 隔离变更 如果项目和程序员之间存在利益冲突,那就能创造出高质量的产品并迅速发展。项目可以强化质量,而程序员也可以提交代码向前进、快速频繁地完成变更。但不幸的是,大多数项目都与之背道而驰,他们将质量控制权交予程序员,满心期盼程序员们会“不作恶”。而这会导致沮丧、痛苦、对犯错的持久恐惧、长时间的拖延、责备和羞辱。最终,项目及其程序员两败俱伤。 快快建好质量墙吧,它既保护了程序员,也保护了项目。英文版原文请点击附件下载
-
本期体验产品:华为云软件开发平台(DevCloud)体验及评测体验形式: 本次体验采用有奖征集体验评测报告+群内交流反馈的形式。我们将在体验官群内(扫描最下方二维码申请成为体验官)筛选15位体验官,所有体验官按照体验要求(活动开始时会在群内公布)体验产品,并输出产品体验报告。我们会从中筛选出高质量体验报告,给予礼品奖励。中奖率超高哟~~☆奖品设置如下☆ 本期所有参与活动的体验官将会同时获得以下2种奖励 华为云定制键盘1个 100元代金券1张产品介绍:软件开发平台(DevCloud)是集华为近30年研发实践、前沿研发理念、先进研发工具为一体的一站式云端DevOps平台,面向开发者提供的云服务,即开即用,随时随地在云端进行项目管理、代码托管、流水线、代码检查、编译构建、部署、测试、发布等,让开发者快速而又轻松地开启云端开发之旅。软件开发平台(DevCloud)使用指南:https://support.huaweicloud.com/devcloud/index.html可用性测试介绍:可用性评测是指通过邀请真实用户,根据典型业务场景进行产品或设计原型的任务操作,对用户使用过程中的行为与想法进行观察、记录与访谈,进而了解产品的可用性水平,发现可用性问题,帮助产品进一步优化体验设计的研究方法。测试维度:我们在DevCloud中选取了以下任务,希望您按照要求完成这些任务。我们将通过zoom在线会议观察并记录您的操作,并在操作完成后询问相关问题。根据您的操作记录和反馈发现我们的产品目前存在的问题,以便我们后期针对性地进行改进,有效提升产品的用户体验。 本次体验范围涵盖开通购买、看板项目、DevOps全流程、管理看板 四个大场景。具体细分为9个小任务:1、开通购买DevCloud2、创建看板项目3、创建工作项4、创建代码仓库5、创建代码检查任务6、创建测试用例7、创建DevOps全流程样例项目8、创建流水线9、定制管理看板体验评测报告交稿时间:用户招募时间:截止7月12日 测试执行时间:7月13日-7月17日,具体执行时间工作人员会与您进行协商 测试方式:可用性测试会采用在线访谈的形式进行,测评期间我们会通过zoom视频会议软件与您进行沟通,期望您能够预留一段较为完整的时间来进行本次测试;2020年7月17日 16:00前,请报名评测的体验官将评体验测报告发帖上传到华为云社区开发者交流论坛中,分类选择(体验官)。并同步微信告知小助手(微信:hwykfz1)微信号。报告形式请以群内通知为准。2020年7月30日 16:00前,将获奖信息告知体验官。体验报告发帖地址:https://bbs.huaweicloud.com/forum/forum-557-639-1.html发帖时,请上传已完成的体验报告,并在帖子内标注微信群昵称,以便评奖时使用 。☆如何报名华为云产品体验官☆请先填写报名表单,报名成为华为云产品体验官。审核成功后,小助手会添加您的微信邀您进入华为云产品体验官群成为华为云产品体验官后续产品体验通知会在体验官群内发布~ 产品体验官可免费参与产品体验并获得相应奖励 扫描二维码,填写报名表
-
软件开发本来具备复杂性特征,随着业务变化加速,层次不穷新技术:5G、人工智能、大数据、AI、物联网的出现,很多软件开发者一直深陷泥潭,处于奔命,996疯狂工作,软件开发效率大幅提升一直是开发者追逐的梦想,如何实现梦想?低代码平台将如何展现魅力?谜底将在本次分享中揭晓。报名链接:https://bbs.huaweicloud.com/signup/d39f9fe29f7e42bf8594cb9d44f8c192
-
《软件开发服务介绍及实战》课程学习进度截图+课程评价截图对课程、云端实验室有什么建议/本次学习的心得体验,改进点?课程很好,对软件开发平台DevCloud的功能都有介绍,学会了很多之前不会的东西,大写的赞!
-
在《人月神话》的开篇提到焦油坑,没有别的场景比巨兽在焦油坑中垂死挣扎的场面更令人震撼。上帝见证着恐龙、猛犸象、剑齿虎在焦油中挣扎。他们挣扎的越是猛烈,焦油纠缠的越紧,没有任何猛兽足够壮烈或具有足够的技巧,能够挣扎束缚,他们最后都沉到了坑底。大型软件系统开发就犹如这样一个焦油坑,很多大型和强壮的动物在其中剧烈地挣扎。他们中大多数开发出了可运行的系统,不过,其中只有非常少数的项目满足了目标、时间进度和预算要求。各种团队,大型的和小型的,庞杂的和精干的,一个接一个淹没在焦油坑中。随着软件开发时间(月)和人员数量(人)的增加,软件开发成果与工作量投入(人*月)一定就会同比增加吗?显然不是,因为人员之间的沟通,分工协作,业务的灵活多变,软件工程师技能差异,新技术如5G、人工智能、大数据、AI、物联网等技术复杂度的增加,太多不确定性因素将导致软件开发成果与工作量(人*月)的投入不成线性增长。这些不确定因素越少,软件开发成果与工作量(人*月)的投入就会接近线性增长,不确定因素如何减少呢?1999年,前甲骨文最副总裁Marc Benioff创立Salesforce,提出“软件终结”口号,面向开发者研发了force.com应用开发平台,基于此快速开发CRM软件系统,开启了低代码应用开发的航程。Mendix低代码领域开发平台成立于2001年,2018年8月被西门子用6亿欧元收购。OutSystems低代码开发平台成立于2002年,2018年6月被KKR和高盛公司联手以3.6亿美元收购。另外,科技巨头们也都纷纷推出自己的低代码开发平台产品,微软在2015年发布的PowerApps、Google2018年开始测试的App Maker等都是低代码产品。在国内,低代码开发平台在近几年也如雨后春笋般快速的发展起来,宜创科技、奥哲、轻流、简道云、APICloud如今都汇入了低代码赛道,巨头科技企业华为,阿里也都纷纷推出了自己的低代码开发平台:华为的AppCube,阿里的宜搭。高盛私人投资公司董事总经理Christian Resch 表示:“我们认为低代码开发领域具有非常显著的市场潜力,大多数全球企业正在将其业务数字化,他们正在尽可能利用软件简化运营、建立新的分销渠道、改善客户体验,以及创造新的产品和服务。”根据Forrester的报告,去年该领域的规模估计为 38 亿美元,预计到 2021 年将增长到 152 亿美元。这些低代码平台的崛起,为什么会被投资者看好,被开发者青睐呢?(点击到论坛参与讨论) “低代码”顾名思义就是开发者写很少代码,通过低代码平台提供的界面、逻辑、对象等可视化编排工具来完成大量开发工作,降低文章开篇提到的软件开发中的不确定性因子,从而大幅度的提升开发效率,让企业能够降低开发成本和价格,降低技术和人员门槛,快速创新应用,实现快速试错,敏捷迭代。低代码平台主要面向如下两类人员提供快速开发应用的能力:1、业务人员,通过提供大量的界面模板、业务模板、流程模板和对象模型,业务人员根据实际业务需要通过积木式组装的方式,就可以快速拼装应用系统,从而实现了应用快速创新。2、软件开发工程师,通过页面编排工具和流程编排的能力,开发者可在平台上组件化、微服务化已有的大量服务,再编写少量代码就可以实现自己想要的应用管理系统。以华为低代码开发平台AppCube为例,其为开发者提供了大量的页面组件、流程编排工具BPM、模型编排工具、基线应用模板、AI服务、视频服务、GIS服务、城市信息模型BIM服务、IOT服务等上千种开放接口,开发者利用这些编排工具,调用已有的大量服务,通过编写少量代码就可以实现自己想要的应用管理系统。除了上述的能力外,低代码平台大多数是以SaaS(Software As A Service)方式向开发人员提供服务,开发人员只申请一个开发者账号,就能使用低代码平台提供的线上开发环境,沙箱测试环境,商用部署环境。开发人员开发完毕后在线编译和打包,通过低代码平台提供的自动流水线,可以将软件包从开发环境部署到测试环境和商业环境。开发人员Anywhere,Anytime就可以开发自己的应用,测试自己的应用,发布自己的应用,所见即所得。但是也要看到,做低代码不是直接去造房子,而是做一套能反复造各类房子的引擎和系统,对平台技术的要求很高,国外的低代码玩家都经历了多年的发展,才走出先平后陡的增长曲线。而在国内,我们还有一段路要走,随着技术的不断发展提升以及各行业数字化转型对软件诉求的增强,低代码开发平台凭借其降低开发工作门槛,缓解成本、人才诉求等优势,减少软件开发的不确定性,使开发工作量的投入与软件有效开发结果向线性靠拢,大幅提升软件开发效率,必定也会走上蓬勃发展之路。那么到底当前各低代码开发平台发展的现状,各厂家的优势在哪里,我们下次再盘。DevCloud 6月闯关训练营正在招募中,在活动期间,各位开发者任意参加各项任务体验、完成推荐任务均可获得不同程度的码豆奖励,更有精美礼品相送!戳链接立即参加!
-
敏捷软件开发宣言我们一直在实践中探寻更好的软件开发方法身体力行的同时也帮助他人。由此我们建立了如下价值观:个体和互动高于流程和工具工作的软件高于详尽的文档客户合作高于合同谈判响应变化高于遵循计划也就是说,尽管右项有其价值,我们更重视左项的价值。Kent Beck MikeBeedleArievanBennekumAlistair CockburnWard CunninghamMartin FowlerJamesGrenningJim HighsmithAndrew HuntRon JeffriesJon KernBrianMarickRobert C. MartinSteve MellorKen SchwaberJeff SutherlandDave Thomasplaceholder著作权为上述作者所有,2001年此宣言可以任何形式自由地复制,但其全文必须包含上述申明在内。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签