模型的过程往往比模型本身更重要,我们不断吸取经验教训,而不是一遍遍重塑模型。
我最近翻阅了一些旧文件,准备把它们丢进碎纸机时,看到了一些关于图形模型的笔记,我停下来对此做了一些评论。
多年来我积累了太多的旧笔记,现在是时候把它们粉碎了。
碎片的照片
当我整理一个名为“测试建模”的文件夹时,发现了一张纸,上面画着以下的图表:
代码聚焦的测试:
系统聚焦的测试:
过程聚焦的测试:
我本来打算扔掉这些笔记,但我觉得这些图表倒是不错地反映了我当时的思考方式。更重要的是,它们是我通过模型学习和理解软件测试过程的一个阶段。我想重新审视它们,看看现在的我对它们有什么新看法。
我没有在这些模型上添加任何文字,除了标题。
这种留有一定模糊性的做法让我可以随着时间的推移重新解读它们,而不会被过多的叙述束缚住。
我在阅读书籍时不做笔记的原因之一是,因为在重读时,我不想被以前的理解或解释所束缚,我希望能够自由地重新诠释。所以,如果我在阅读书籍时确实做了笔记,我会把它记在一本单独的笔记本里,可能还会把笔记页撕下来,放在书的末页。
探索命名
看着这些图表,我觉得当时我是在探索命名。我试图为这些图表命名,使用的是测试范围的现有标签,比如:
- 单元测试
- 集成测试
- 系统测试
- 等等
我并不是在“定义”这些名称,而是在建模它们在测试过程中的范围或焦点。
虚线表示了名称所暗示的“范围”。
例如,当进行系统测试时,图表中的范围涵盖了输入 -> 过程 -> 输出,但不包括上下游系统。尽管图表显示,它可能包含对消息响应的处理,以便完成系统范围内的流程。
重新绘制照片
我花了一些时间在Miro中重新绘制这些照片,使它们更清晰,以便看看现在对它们的理解。
在此过程中,我对一些命名进行了修改。
集成测试 变成了 系统集成测试。
我还扩大了系统的范围,以表明我对外部消息的响应很感兴趣。
系统聚焦的测试
这里的系统聚焦范围从系统输入到输出和响应。
这让我感觉测试的每个步骤都会从系统输入开始。
这个图表提出了这样一个想法:测试可以在一个集成环境中进行,包含多个系统,或者可以用模拟系统替代,因为这里的焦点是系统内的流程和对输出的响应。
我可能会倾向于将这个模型解读为:每一步测试都是独立的。比如在一个“干净”的系统环境下,输入一个数据,触发处理和响应,等待处理完成后检查结果。
但这个模型并没有告诉我们具体该如何进行测试,只是指出测试焦点可能是“系统测试”。
我们可以输入多个数据,模拟不同顺序的响应,观察对功能的影响。
此外,这里没有说明基础设施或我们可能在哪些位置观察或检查结果。我的理解是,系统处理过程中发生的任何变化都可以作为观察和检查的对象。
系统集成聚焦的测试
在这个图表中,焦点是系统之间的通信。
我觉得系统之间的箭头应该是双向的,涵盖消息的发送和响应。
但这个图表表明我们将测试范围缩小到仅需进行足够的系统处理以生成消息,以及足够的处理来触发响应。
这种焦点缩小意味着我们依赖其他测试模型和活动来涵盖实际的系统处理,而这里我们主要探索的是系统间能否有效通信。
在这个模型中,没有对消息或响应的模拟或假设。实际的系统将生成消息,并真正处理它们。
而系统聚焦的模型允许进行模拟和假设,因此基于合同的自动化测试更适合系统模型,而不是系统集成模型。
用户验收测试(UAT)聚焦的测试
过去十多年里,我没有在任何组织中见到过进行UAT。
但“UAT”模型对我来说很有趣,因为它的测试范围部分覆盖了系统功能。某些纯自动化或系统间的功能被排除在外,因为它们不会对用户的实际工作产生实质影响。
现实世界的交互由“手动过程”的椭圆表示。
因此,这一焦点不再是关于“验收”,而更多的是探索系统在用户实际工作中是否适用,以及系统必须支持的所有外部通信活动。
当我绘制这些模型时,我使用了当时通用的名称,但我真正探讨的是测试可以覆盖的范围。
这种范围,包括现实世界的交互和部分系统覆盖,是大多数项目中容易忽视的。
单元测试
图表中没有任何限制范围的内容。
“代码”指的是类?还是类的集合?或者函数?
图表没有限制或偏向使用模拟或替代。
“模块”指的是什么?我猜当时是指逻辑类的集合。它们可能映射到逻辑设计中的部分,比如领域对象、API或者持久化机制?
不过我从中得到的核心是,它关注的是“内部”交互,无论在哪个抽象层次或接口上,测试的焦点是代码及其交互。
链路测试
链路测试这个词是否存在?
我猜当时我担心单元测试可能会忽略逻辑层的交互,所以我仍然想看到代码在没有系统概念下的集成。
测试的阶段
“阶段”图表对我来说很有趣,因为我没有看到任何“阶段”。现在看这个图表,我在想自己是不是试图表明,命名阶段并不重要,确保覆盖所有不同的焦点视角更为重要。
这更像是我现在的测试观。阶段这个概念不再特别吸引我,覆盖面则更加重要:
- 确保我们在正确的层次上进行了测试,探索并识别问题
- 确保我们在正确的层次上实现了自动化,以识别变化
- 确保我们在适当的抽象层次上实现了自动化,使其在执行时易于维护,并允许重构,以便随着系统演进进行调整
缺少了什么?
有很多东西缺失。
这里没有关于基础设施的表示,但我认为在以下情况下应该会考虑到:
- “系统集成”聚焦时
- “链路”聚焦时,我们在逻辑层、内存层和集成层有一部分覆盖(例如,使用TestContainers启动Postgres、MySQL或LocalStack)
这里没有性能测试或安全测试的表示,但这些测试同样可以在单元、系统或集成的焦点下进行。这取决于我们使用的工具以及我们愿意接受的风险。
此外,命名对我来说已经不再重要。
现在对我来说重要的概念是:
- 隔离环境下的测试
- 使用支架(scaffolding)进行测试
- 集成测试(使用“真实的”东西,而不是支架)
- 扩展集成,尽可能减少支架,最终实现无支架的测试
- 将“现实世界”和“实际流程”包含在测试范围内,而不仅仅关注系统本身
测试的顺序对我来说已经不再重要。重要的是,我们应该退后一步,审视测试覆盖的空白,并识别风险。这样在任意一个阶段结束时,我们能够看到我们覆盖了所有关键部分。
欢迎对这些图表提出批评
如果你愿意,可以对这些图表提出批评。我对这些图表没有什么情感依附。我认为这些图表大约是在15到20年前由我绘制的,所以它们是历史文档,主要目的是展示我过去通过模型来思考测试的方式。
与其批评它们,我更希望你分享自己的模型,或者构建属于自己的模型,然后在几年后重新审视这些模型。希望你会发现你已经不再是过去的自己。
正如威廉·布莱克在《耶路撒冷:阿尔比恩巨人的外化》中写道:
“我必须创造一个体系,否则我将被他人的体系奴役。我不会推理和比较:我的任务是创造。”
如果我们的任务是创造,那么随着我们不断创造,我们自然会改变和进化支撑自己体系的模型。当我们回顾过去的作品时,应该能够看到自己已经改变了模型以及我们对它们的诠释。否则,我们可能会被自己的体系奴役。