更大的测试
即使有模拟,单元测试也不能满足我们所有的需求。
为了检查我们的代码与其他代码的行为——或者我们的代码需要使用的外部服务,集成测试是必要的。然而,为了测试整个软件,我们需要系统测试。 我们可能想要编写更大的测试还有许多其他原因。
Why Write Larger Tests?
(1) Fidelity
(2) Unfaithful Doubles
(3) Configuration Issues
(4) Issues that arise under load
(5) Unanticipated behaviours, inputs and side effects
(6) Emergent Behaviours
保真度Fidelity——环境的现实主义
保真度是测试反映被测系统真实行为的属性。
设想保真度的一种方法是根据环境:
• 单元测试捆绑了一个测试和一小部分代码一起作为一个可运行的单元,确保代码被测试,但与生产代码的运行方式有很大不同。
• 生产是测试中保真度最高的环境,但是不应该在那里进行测试! 最好的选择是系统的阶段版本,即系统在其实际无需使用(或丢失)真实数据。
测试也可以根据测试的忠实程度来衡量内容是真实的。
许多手工测试被工程师驳回,如果测试数据本身看起来不现实。
从生产中复制的测试数据更忠实于现实,就这样被俘虏了。
一个很大的挑战是如何在启动新代码之前创建逼真的测试流量.
Unfaithful Doubles
Doubles并不完全复制他们正在加倍的东西。这可能会导致单元测试出现漏洞。
此外,doubles和他们的原件可能会变得不同步,特别是原始级的作者是否也不负责double。
配置问题Configuration Issues
单元测试无法测试与整体相关的配置问题系统。
在谷歌,配置更改是公司经历的许多重大中断的首要原因。
2013 年,谷歌全球停电,原因是一个糟糕的推送了从未测试过的网络配置。
仅在负载下出现的问题Issues that Only Arise Under Load
单元测试无法检查系统性能,尤其是它在极端负载下的性能(这称为压力测试)。
大容量! 每秒通常有数千或数百万个查询。
意外的行为、输入和副作用Unanticipated behaviours, inputs
and side effects
单元测试受限于编写它们的工程师的想象力!
用户在产品中发现的问题大多是意料之外的(否则他们会被测试!)
这就是自动化测试技术(和人工智能)变得有用的地方——for example, fuzzing.
紧急行为Emergent Behaviours
单元测试仅限于它们涵盖的范围,因此如果行为更改超出此范围,则无法检测到。
而且因为单元测试被设计成快速可靠的,它们有意消除了真实依赖、网络和数据的混乱。单元测试就像理论物理学中的问题,隐藏在真空中,巧妙地隐藏在现实世界的混乱中。这对速度和可靠性非常有用——但会遗漏某些类型的缺陷。
这些理由并不是说“不要做单元测试”,而是应该使用更大的测试来补充单元测试。
某些类型的系统测试
浏览器和设备测试Browser and Device Testing
浏览器和设备各不相同,通常会决定用户的交互方式
与应用程序。例如,在给定屏幕尺寸的情况下,可以呈现什么/多少内容。
掌握所有这些版本的设备可能会出现问题——导致建立公共设备库。
性能、负载和压力测试Performance, Load and Stress Testing
这些测试对于确保版本之间的性能不会下降以及系统可以处理预期的流量高峰至关重要。
部署配置测试Deployment Configuration Testing
通常,缺陷的根源不是代码,而是配置:
• 数据文件
• 数据库
• 选项定义
更大的测试可以测试这些东西,因为配置文件是在产品二进制文件启动期间读取的。
这些测试通常是系统的冒烟测试,不需要太多额外的测试数据或通过断言进行验证:如果系统成功启动,则测试通过!
冒烟测试是指对系统或软件应用程序执行的初步和基本级别的测试。 冒烟测试的主要目的是快速确定系统或软件是否足够稳定以进行更全面的测试。
典型的冒烟测试场景可能包括:
通过进行冒烟测试,测试人员或开发人员可以快速识别可能阻碍进一步测试过程的任何严重问题。 如果系统通过了冒烟测试,则表明它足够稳定,可以进行更全面的测试,例如功能测试、回归测试或性能测试。 如果冒烟测试失败,则表明在继续进行进一步测试之前需要解决一些重大问题。
探索性测试Exploratory Testing
探索性测试是一种手动测试形式,侧重于通过尝试新的和已建立的用户场景来寻找有问题的行为。
训练有素的用户/测试人员与产品交互,寻找通过系统的新路径,这些路径的行为偏离预期或直觉行为 - 或者是否存在安全漏洞。
用户验收测试User Acceptance Testing
单元测试是由开发人员编写的,因此很可能对系统预期行为的误解不仅反映在代码中,还反映在单元测试中。
用户验收测试对系统进行测试,以确保特定用户旅程的整体行为符合预期。
据说这样的测试从“端到端”运行系统并且是有时称为端到端 (E2E) 测试。存在多种框架,例如 Cucumber 和 RSpec,可以使测试以用户友好的语言编写和读取。
灾难恢复Disaster Recovery
这些测试揭示了系统对意外情况的反应有多好, 数据中心火灾、恶意攻击等变化或故障,地震等
系统被移除或拆除以观察实践中发生的事情的影响。
这通常也会考验组织的脆弱性——因为关键决策者可能会突然失去联系。
混沌工程Chaos Engineering
因 Netflix 而流行的混沌工程通过编写模拟实际湍流条件的程序来测试系统的弹性。这些程序引入故障/问题,包括服务器关机、延迟注入和资源耗尽。
这些故障比突变分析引入的故障级别更高、更有针对性
大型测试的问题
较大测试中的脆性Brittleness in Larger Tests
较大的测试也无法避免脆性问题。
回想一下,脆弱性是指测试由于代码的内部更改(重构)而失败,但不影响其外部行为。
例子:
在端到端系统测试中,测试可能与 GUI 元素的名称或其布局过于紧密——这意味着测试失败然后 UI 发生变化。
片状测试Flaky Tests
较大的测试更容易出现片状问题。
不稳定是测试或代码中的非确定性导致测试失败的结果,而实际上它们是有效的(并且不太常见:反之亦然)。
例子:
执行一些需要服务器响应的代码的系统测试。 服务器运行时测试通过,服务器宕机时测试失败。 但是,当测试失败时,代码中没有错误。
单元测试也可能不稳定,但由于它们倾向于关注逻辑而不涉及非确定性元素,因此这种情况不太常见。
片状测试引起的问题
片状测试失败的间歇性让开发人员抓狂,因为片状测试的根源通常很难追踪:问题的症状(失败的测试)通常与非确定性行为的根源相去甚远,尤其是在系统测试中。
但是一旦一个测试套件有几个不稳定的测试,开发人员往往会停止信任它和/或运行它。
片状测试是连续构建失败的常见因素在集成服务器。
问题:
(1) 我测试了一些依赖于星期几的代码。为什么我可能是片状的?
(2) 我测试了一些涉及启动和执行多个线程的代码。 为什么我可能是片状的?
(3) 我测试了一些代码,这些代码依赖于在特定时间范围内执行的某些任务。 为什么我可能是片状的?
(4) 我测试了一些检查最新新闻项目的代码。为什么我可能是片状的?
大型测试的问题
(1) 可靠性Reliability ——较大的测试可能是脆弱的和片状的
(2)速度Speed——较大的测试可能会很慢,打断开发人员的工作流程
(3)所有权Ownership——单元测试有一个所有者,谁负责系统测试?
(4)标准化Standardisation——单元测试有一个标准的框架和形式。 大型测试需要应对不同的基础设施和环境,以及多种相互竞争的工具和框架(其中一些是内部开发的)。