仓库源文

.. Kenneth Lee 版权所有 2024

:Authors: Kenneth Lee :Version: 0.1 :Date: 2024-06-12 :Status: Draft

一个简单的复杂视图建模例子


本文记录一个简单的复杂视图建模例子,以便未来介绍类似4+1视图这样的建模方法的时 候使用。这个叙述有点怪,把简单和复杂两个概念放在一起,也许读者看完全文自然就明 白具体的意思了。

我们的需求很简单:我们要做一个芯片验证测试套。比如说,我们做了一个新的芯片,这 个芯片是否符合手册定义的需求呢?我们需要根据接口测试一下,看看功能在不在,是否 和定义的一样,这样我们就知道这个芯片做得有没有问题了。比如芯片手册定义了一个加 法指令add,我们至少得试一下,正数加正数是不是对的,正数加负数是不是对的,正数 加0是不是对的,相加溢出的时候是不是对的……等等。

这立即可以编码,写个汇编,设置几个寄存器,调用add,然后验证一下结果和预期是否 一样,简单得很。

但这个例子建模相当复杂,我们就拿4+1视图方法为例吧。先初步建模一下Use Case图。 这是建模范围和需求逻辑了。我们前面描述需求的时候用的是所谓的“原始需求”,就是简 单想到啥是啥,只是无逻辑的“欲望”的表达,我们基本上没有给边界,只是说些例子,大 家有个“差不多”的理解就行了。

但这样做出来的东西很难有共识的。我简单细化一下你就知道了:这到底要对着哪个手册 去定义测试标准?芯片手册不会定义BIOS的接口,那么我们的测试软件怎么初始化?初始 化的软件算不算我们交付的一部分?有些功能是综合测试出来的,比如虚拟化功能,你总 得写一个简单的虚拟机才能验证的,这个虚拟机是不是交付的一部分?

你看,这种问题,你不能说“都知道”吧?Use Case视图,是建模这些问题的,那些UML的 Use Case图的形态本身根本不重要,我们分出几个实体,用Use Case标识它负责什么,然 后靠文字或者更多的表述手段说清楚这个功能的细节而已。你不能把这个当作写程序,以 为自己定义了一个什么头文件才行。

好了,假定现在我们知道我们要做什么了,前面我提到的那些东西也有共识了。是不是就 可以马上动手写程序了呢?

别急,要不你先给我说说,如果我是这个测试套的用户,大致怎么用这个测试套行不?

“把测试套加载到要测试的机器上运行,如果有错误就会输出具体是那个用例出错,如果 没有就会最终输出‘测试成功’。”

这个描述有点模糊,但不要紧,我们就在这个模糊的层次上谈,我就问问:我被测试的机 器连串口都没有,你怎么“输出”?就算我有串口甚至有屏幕,你没有我的驱动,你是怎么 “输出”的?

还有,什么叫“用例”?一个add指令是一个测试用例,还是一个虚拟化功能是一个测试用 例?你告诉我“用例错了”,我怎么判断我的芯片具体做错了哪里?

你看,你给我的承诺根本不严肃,你对我这边发生什么事你都搞不清楚,你的软件要覆盖 多大的范围也不清楚,你就敢开始动资源给我开发了?是欺负我善良最终肯定会给钱吗?

好,现在你修改一下设计描述:

“我们会提供一个‘测试库’,提供相关API手册,测试者需要基于这个库开发一个可以在被 测设备上运行的Image,适配输出函数,并在Image中启动一个循环,逐个调用预设的测试 用例,每个测试用例会输出测试结果。测试用例以源代码的形式提供,使用者可以通过查 阅用户手册和代码找到错误的原因。”

啊,你看到了,你现在写的这个就是一个简单的“概念视图”了。

所谓“概念视图”,就是“我说的那个名字具体是什么意思”,而不是形式上的一张UML类图。 UML类图最多就是表达一些这些名字的关联关系,不是概念视图的的核心。你要说清楚你 的各种接口,包括其他视图的各种含义,你就要解释你的“测试用例”是什么意思,Image 是什么意思,“测试库的代码”,“Image的代码”,“驱动的代码”是什么意思。而且,只要 你开始写这些东西是什么意思,你就不得不发现,你还要说清楚“被测试的设备 (Target)”,“编译测试套的设备(Build)”,“获取输出的设备(Host)”是什么意思…… 这些“意思”,就是概念视图要建模的模型。这个模型用来理清楚概念之间的边界在哪里, 是否有重复,是1-1还是n-m的对应关系。谁会创建谁,谁会使用谁,谁和谁没有关系。

现在是不是开始头晕了?你也许会想:这么简单一个程序,不至于。

那我就要问这个问题了:这个程序是不是这么简单?

如果你说,我们已经知道我们这个测试套就是用一轮,我们有一个产品马上要上市了,我 们就测试一下,不用于其他环境,也没有什么用户,就自己用,不考虑这些问题行不行?

行!太行了。你早说啊,为什么要端着呢?你不要把自己称为“某某平台测试套”啊,你这 就是“某某芯片某某版本测试方案”。回忆一下我们前面的Use Case的建模,你的模型是不 是就完全不同了?边界是不是就完全不是那个样子了?

但你是不是就不用建模了呢?当然不是啊,你要写数千个测试用例的啊。你要不要根据你 的目标,决定一下你的用例是自动生成的,还是手工写的?要不要建模一下你对输出模块 的要求?前面提到的,谁是Target,谁是Build,谁是Host是不是还要说清楚?print到谁 上面,在谁上面生成报告,谁上面可以运行Python,谁上面只能运行汇编和C,是不是要 理清楚?否则一旦你开始编码了,谁都可以print,你到底应该调用哪个print,才输出到 你要的位置上?

我觉得这个例子好,是因为它确实是个需求挺简单,但真建模其实很麻烦的事情,因为能 把事情说清楚就很不容易。我这里一步步给你呈现问题,也许你还会觉得这个问题你看懂 了,但那是因为我的建模,如果我换一个方法建模,也许你看到的问题就不是这个问题了。

所以,建模就是这样一个东西:它是一种创造,这种创造通过把不同的内容分解到不同的 概念上,从而制造了你“对这个事情的看法”。它本身就是误导。但没有这样一个过程,你 对整件事情就没有认识,你也无法解决它了。建模方法难教就难在这里:教就要说清楚问 题,但问题说清楚了,就没有什么需要教了。

换一个方法建模呢?整个问题就呈现了不同的样子(因为新的建模方法的名字和其他建模 方法可能范围就是交叉的),根本不能用来对比。就好比前面这个问题,我不用测试用例 来建模,我用“功能模块”来建模,说要开发一组同功能的不同算法,然后用多个算法的结 果比较来判断芯片设计是否正确,然后我用数学模型来证明这也可以说明芯片功能是否正 确,你能比较两个模型的优劣、工作量、第三方依赖成本、测试时长吗?