对实例化需求方法的整理与思考
作者:网络转载 发布时间:[ 2016/6/16 15:28:21 ] 推荐标签:软件测试管理 需求管理
用事例阐明需求的具体内容
只有当场景描述具有很强的带入感时,才能激发客户参与讨论的热情,才更容易达成共识,并发掘潜在的概念和需求。传统的基于平面文档的平铺直叙的方式,在向用户展示系统场景、捕捉系统需求时,可能会因为词不达意,而导致不同的人产生不同的理解,所以描述性的文档始终无法与清晰的代码媲美、也远没有代码直接。然而清晰的代码并非一朝一夕,让用户直接面对系统代码也完全没有意义,所以我们退而求其次,改用一个特定场景下的具体事例来表述系统的行为,达到与客户有效交流的目的。所以,举例说明的方式,对于共同认识和理解某个场景是非常有益的。
在选择和描述每一个例子时,作者提出要坚持“例子四原则”:
· 例子总是明确的。
要使用"yes or no"这样的问卷调查,应更注意彼此的沟通。
不要使用“小于10”这样的『比较性』描述,而使用“9”、“11”这样明确具体的值,来对应不同条件下的场景。因为比较性的描述,总是意味着一个值域,而不是单个的值。相比引起变化的单个临界值,这样的值域对于我们理解事例并没有更多的帮助。
· 例子总是完整的。
要用具体的数值去表述不同条件下的不同场景,特别是要注意正反两方面的数据输入组合。对负数、0这样的边界值给予足够重视。当涉及的条件是对象时,则要留意无效的引用、null等。
使用替代的方法进行验证。这个小节在原书的描述里非常晦涩。我大意理解为,对于一些新旧数据并存的系统,我们很容易只惦记着新环境下的各种例子,而遗忘了旧数据也应该当被考虑在内。
· 例子总是现实的。
建议直接使用真实的数据,而不用费心为测试专门编造数据。这样可以充分利用旧数据,减少未来可能的数据兼容风险,保证新旧数据在系统中的一致性。
直接由用户提供基础的例子,而不要自己臆造。
· 例子总是易于理解的。
不要拿一堆的参数组合表格给客户,我们重点关注的应当是所有的临界点。对每一个临界条件,都应当进行认真讨论。如果双方对该条件是否确系临界条件有争议,那说明双方对例子的理解本身是有误解的。这似乎又回到所有问题都要达成共识的这个原点上来了。
注意寻找潜在的概念。在面对与一个功能联系的一大摞事例时,解决事例过于细碎、不易理解的方法,在于对事例适当进行抽象和归纳,然后再转头分析此前那些琐碎的下层概念,如同是切碎了再进行二次理解,这样可能会发现一些潜在的概念。
在安全、性能等非功能性的需求方面, 当其重要性已经达到影响业务价值或者业务目标实现程度的时候,那清晰地表达出来。这与《UML精粹》中的观点是一致的,一切需求的重要性视其对实现业务价值、业务目标的影响程度而定。在性能、响应时间这些无法准确表述的需求方面,作者引入了QUPER模型。对这个模型,我个人理解是预估这些指标对应的障碍,以及由此产生的开销,然后再展开讨论。每个问题会被分成三个方面:
可用性:是否有功用性——“能不能用?”
分化性:是否有市场占有能力——“相比其他产品是不是更具优势?”
饱和性:过度设计没有意义——“弄得再好一些有没有实际意义?”
作者使用了“手机开机速度”作为例子:不能开机,手机没用;开机速度太慢,没市场竞争力;开机非常快了,再快没有意义了。
提炼需求
好记性不如烂笔头。交流的结果一定要以某种形式记载下来。原始的例子象未经雕琢的钻石,只有提炼后才是关键的、易理解的、方便转换为可执行Specification的、能予以自动化测试的Key Example。
Specification应该是明确的、可测试的
这一点,和Example的”明确的”是同样的含义。要尽可能消除描述上的模棱两可,并且要保证所有参与讨论人员认识上的一致。
Specification应当是真实的互动,而不是简单的脚本
脚本通常更侧重于描述一个事物是如何变化的,更多的倾向于流程方面的内容。这也是客户在描述的时候,容易掉入的一个陷阱——“先这样,然后那样,接着再怎么样,后又是什么样”。这种脚本或者说是流程形式的表述,缺乏一个系统的视角,缺少对系统与用户交互情况的表达,而且流程本身很容易改变并且难以维护,所以并不适合直接作为Specification。正确的方式,应该按”在这样的情况下,系统会做出那样的反应“的形式进行表述。重点是”系统应该做什么“,而不是”系统应如何工作”。
Specification应该是业务功能相关的,而不仅仅是软件设计意义上的结果
Specification不要与代码、与UI等技术实现细节耦合太紧。技术层面的难题,以及流程等细节,留待再下层的自动化测试去解决。
Specification应该是自解释的、不言自明的
为了提高Specification的可阅读性,可以给它增添一段描述文本,然后交给其他人看,静静地观察对方的反应。如果对方无需额外提问能理解并达成一致,那说明这个Specification符合预期。否则把回答对方的解释,也写进开头的这段描述性文本里。
在筛选关键事例时,应优先把握所有成功的场景,而把可能失败的情景先放在一边,由简入繁地先把功能正常地展现出来。在具体筛选时,可以从以下几个方面着手:
描述了业务功能的某个方面
描述了重要的业务边界或者业务规则
描述了可能导致失败的某种情形
Specification应该是专注的
使用Given-When-Then三段式表述Specification,并且尽量避免考虑动作或者事件之间的依赖关系,好专注于一个动作、一个事件。对于新旧数据共存的系统,比如ES+CQRS里新旧版本的领域事件,为了保持专注,建议把这种兼容性问题压入自动化测试层去解决。对于系统当中的缺省值,虽然能让Specification更易读,但考虑到这种缺省值如果表述在Specification中,将会导致过强的依赖性,所以也建议移入自动化测试层。在这个问题上,可以参考“魔数”。当我们把魔数显式地定义出来时,才更有助于我们消除误解。将其移动到自动化测试层,或者放入一个全局的配置当中,都是相对更灵活的方法。
Specification应该是具备领域意义的
这一点又转回到DDD了,即Specification中引用的概念、关系,都应该与当前上下文中的通用语言保持一致。
用自动化测试验证需求
随着软件规模的逐渐增长,测试的数量、大小、应对变化的能力,都要求我们采取自动化的测试方式。在这个过程中,必须以预定的Specification不再修改作为前提,否则我们的自动化测试只能是以讹传讹了。换个角度看,虽然自动化测试增加了学习的成本,要引入额外的BDD工具,编写额外的Feature与Specification,得到的却是前后一致的需求表达和更加轻松的后期维护,代码的实现也会更自然。这一点,我认为和DDD里的从UL到代码的展开是一致的。因为有统一的UL和清晰的模型,所以直接映射到代码也更直观和自然了。
在具体实施自动化测试时,应该由简入繁、从易到难,事先做好规划。因为构建整个自动化测试的上下文环境是相对比较耗时费力的,这个上下文还要集成到一定的系统环境中才能执行,将来需求发生变化时这个环境也能被重用,所以非常有必要在对待整个测试环境规划时更慎重一点。
在具体的实现环节,将自动化测试与编写业务代码同步,比如采用TDD的方法,能让所有人把精力集中在测试上,保证Specification顺利实现。这如同公交车,如果中途没有乘客上下,自然会跑得很快。但事实并非如此,测试的职责是保证Specification的实现、业务代码的正确,所以自动化测试的规划与实现,必须要由开发团队承担起来,不能交给其他人。
手动测试与自动化测试的区别在于,手动测试侧重于准备上下文,然后测试是否通过,关注的是成功与否;自动化测试则关心导致测试失败的原因。特别的,手动测试通常是脚本化的,一个步骤紧跟一个步骤,每一次测试都重复这个过程。如果测试失败,那么手动检查其中的每个步骤也在情理之中,反正都是手动的。自动化测试则必须消除这种测试步骤之间的依赖性,否则当测试失败时,无法确定究竟是哪个步骤出了问题,自动化测试将因此退化成手动测试。如果遇到这种情况,可以将其切分为若干个小的测试,比如每个步骤对应一个小的自动化测试,改由测试上下文通过准备测试条件将这些小测试联系起来。由此引申出一个问题:测试代码要不要良好的组织与设计?答案是肯定的。因为良好编码的测试代码,才能方便维护和阅读,并作为活文档的提炼来源。
Executable Specification通常是用文本或者HTML格式进行描述的(想想Cucumber的Step或者Spock里的测试方法的描述式命名)。这样当这个可以执行的需求说明发生改变时,通常不需要重新编译业务代码。而自动化测试是代码,并负责对Specification的验证,所以当需求说明改变时,要重新编译。此时,为了避免在Specification中混杂太多计算、判断的逻辑,要把这些验证逻辑放在自动化测试里,而不要表述在Specification里。因为对于Specification而言,在转换为Executable Specification时应当关注的是“测试什么”,而把“如何测试”的责任交给自动化测试。
尽管测试不能是脚本,容易变化的流程应尽量放在自动化测试层。但是无论怎样,业务流程总是客观存在的,通过When-Then的事件驱动方式,我们可以借由若干个Specification的组合,展示完整的业务流程。而具体的业务逻辑,则放在Specification里。所以,不要在测试代码里重复业务流程或业务逻辑。
UI的自动化测试
依赖UI与数据库的测试,是自动化测试面临的大困难。书里提到了许多建议,但都非常需要实践进行验证,才能深刻领会。至少我只理解了皮毛,所以这一段的内容暂时没有办法总结。尽管如此,大量地引入Stub与Mock进行测试、隔离UI与业务模型、进行持久化无关的设计、建立统一的应用服务层、在Specification里竭力避免引入UI与存储相关的元素等等,都是可行的方案,类似MVC、MVP、MVVM等模式也将成为我们解耦的利器。
即使要对UI进行自动化测试,也建议使用针对UI编写的Specification进行验证,而且不要使用“录制-回放”工具。因为这类工具生成的脚本通常会增加一定的学习成本,而且会非常难以理解,不便于维护。
从需求说明到UI的自动化测试,可以从以下3个抽象力度逐渐弱化的不同层次进行实现。其中,需求说明应该在第一个业务规则层中描述,自动化层则应该通过组合第三个技术行为层来表达第二个业务工作流层。这样分层实现的从需求到测试的描述,更易于理解,也更高效。
业务规则层 :测试要展示或者操作的是什么? 对一条业务规则进行描述:对购买了5本书的客户提供包邮服务。
业务流程层 :如何通过UI使用某个功能,以更高的抽象级别进行描述? 对一个业务流程进行描述:放5本书进购物车,然后验证是否提示已包邮。
技术行为层 :在一个流程的某个环节,需要哪些技术性步骤?对实现一个业务流程的具体UI操作步骤进行描述:点击5本书的"放入购物车"按钮,然后点击“下单”,页面显示“已包邮”。
持久层的自动化测试
用数据库作为自动化测试的数据来源,是一个比较便利的方式,但是如何管理这些数据却成为一个难题。要避免直接使用旧存的数据作为测试的数据源,因为这种数据与现在的需求可能存在冲突、不易理解。对于构造过程相对复杂的对象图,可以尝试在数据库里预置相关数据,以提高自动化测试的速度。
在重构需求的同时,频繁地进行同步验证
这一部分的内容,与重构、持续集成紧密相关,因此提到的也多是化整为零、“不要想一口吃成一个胖子”、用Mock隔离故障点、以事件驱动测试、先保证同步再尝试异步测试等等建议,并且提倡引入并发测试、快慢分组等方式尽快提高测试的反馈速度。
“业务时钟”
对于那种周期性执行的测试,引入自然时钟显然是不合适的,所以新增“业务时钟”的概念去控制这个周期,使之可以根据测试要求随时执行这一类的测试。我的理解,它等同于一个虚拟的时钟,基本原理还是触发一个时间事件,然后利用这个事件去驱动测试。
提取活文档
由于提取活文档更多的是BDD工具的使用,所以只要有编写优雅的Specification和自动化测试作为基础,文档的生成是一件水到渠成的事。在这个部分,主要的建议包括:
以UI导航流程、功能结构、业务过程等组织文档内容。
即便是虚拟的角色,也要保证完整的角色信息。
合理使用Tag、WiKi等一些文档组织技术,提高可阅读性。
在文档中始终保证领域专用语言DSL的统一。
写在后
实例化需求的实践,重点和难点都在其中的4个环节:制定需求、描述需求、提炼需求和自动化测试。而《实例化需求》这本书本身的内容,也更偏重于用正反两方面的具体事例来引导我们的思考,涉及具体操作步骤的内容相对较少。所以和DDD一样,掌握实例化需求的方法,也需要大量的实操和经验积累。整理出这篇书摘,附上自己阅读时的注解,希望可以抛砖引玉、温故而知新。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南