很好,但有这么多基于模型的测试(MBT)的替代品,你如何在其中选择? 我向你展示不同的方法和它们的优点和缺点。
MBT的方法有不同的分类。例如,建模语言,文本与图形,模式,即在线或离线,等等。这里我介绍另一种基于测试自动化的效率和可用性的分类。在这个分类中,只有三类。
- 无状态
- 有状态的
- 聚合的
这里我只考虑前两类,在以后的博客中再考虑第三类。
无状态的基于模型的测试
MBT方法的第一类是无状态MBT。这里的业务流程被建模,描述了系统的动态方面。这些模型的例子有BPMN、UML活动图、用例等。所有这些解决方案都有不同的符号,但在它们所涉及的信息方面却非常相似。所有这些都由用户行为、事件和可能的系统响应组成。 更重要的是它们不包括的内容:状态。大多数MBT工具都使用这种技术。这些模型在软件工程中被成功使用,你可以认为它是MBT的完美解决方案。不幸的是,当用于测试时,有一些问题。
任何无状态模型都可以转化为类似的图,见下面的例子。测试是基于一些图的遍历和测试选择标准而产生的。第一个问题是,图中存在不可行的路径。 为了避免这个问题,MBT工具提供了约束的用法。约束是禁止无效路径的条件。例如,一个约束条件是过渡b不能在过渡a之前。
然而,有时也需要防护条件。这里的保护条件描述了一个给定的动作/事件何时可以发生。这意味着建模需要一些编码。使用这种方法,MBT工具不可能是完全无代码的。
然而,这些MBT方法更大的问题是,由于它们不考虑状态,它们甚至可能找不到一个简单的bug。例如,一个经常出现的错误是,当一个代码位置在第一次遍历时有一个正确的状态,但在随后的一些遍历中变得不正确。例如,在20欧元以下是不可能付款的,但是增加食物达到21,然后删除一个项目到20以下,付款仍然是可能的。
为了证明无状态MBT,这里有一个简单的例子。
一家租赁公司借出汽车(300欧元)和自行车(100欧元)一周。
R1客户可以将汽车或自行车逐一添加到租赁订单中。
R2客户可以将汽车或自行车逐一从租赁订单中删除。
R3如果客户以700欧元租用车辆,那么他们可以免费租用一辆自行车。在折扣的情况下:
R3a如果客户之前已经选择了一些自行车,那么其中一辆就会免费。
R3b如果客户之前没有选择任何自行车,那么就会增加一辆免费的自行车。
R3c当折扣被撤销(见R4)但又被给予,同时又没有增加自行车,客户会拿回之前的折扣。
R3d当折扣被撤销,并增加了一些自行车,当再次给予折扣时,那么其中一辆就变成了免费的。
R4 如果客户从订单中删除了一些汽车或自行车,使折扣阈值不成立,那么免费的自行车将被撤销。
这里是上述需求规范的一个简单的无状态(或流)模型。边缘是用户行为,节点是系统响应。

这个模型允许任何有效的测试案例,因为任何添加汽车/添加自行车/删除汽车/删除自行车的序列都可以在图中被遍历。从一开始,只有一辆汽车或一辆自行车可以被添加。很好,你可以认为这个模型很好,但事实并非如此。
如前所述,有一些无效的路径导致了非可实现的测试。例如,你可以穿越添加一辆车,然后删除两辆车的路径。然而,相关的测试会导致购物车中的汽车数量为负数。一般来说,你只能从购物车中删除现有元素。因此,模型中存在几个无效的路径,使用约束条件是不够的。
相反,需要防护条件,也就是说,建模需要一些编码。例如,"删除汽车"的过渡从节点 "增加的汽车"以外的地方开始,其保护条件是。
number_of_cars≥1。
因此,必须对变量进行处理并使其保持最新状态,例如。
number_of_cars++或number_of_cars-。
当删除自行车发生时,我们应该添加类似的代码和防护条件来过渡。
如前所述,一个更重要的问题是,这个方法不会发现其他方法会发现的一些bug。为了了解原因,让我们选择 "所有过渡对 "的标准,即所有相邻的过渡/边缘对都应该被覆盖。尽管在扩展图中这样的对的数量很多(16个),但测试仍然是不可靠的,也就是说,不会发现错误。
从需求中可以看出,你应该测试以下内容。
T:
- 增加一些车辆以增加一辆免费自行车。
- 删除一些车辆,直到免费自行车被撤回。
- 再次添加一些车辆,看免费自行车是否被送回。
然而,满足该标准所需的过渡对是。
- 增加汽车,增加汽车,删除汽车 (2)
- 添加汽车,添加自行车,删除汽车(2)
- 添加自行车, 添加汽车, 删除自行车 (2)
- 增加自行车, 增加自行车, 删除自行车 (2)
- 添加汽车,删除汽车, 添加汽车(1)
- 添加汽车,删除汽车**,添加自行车**(1)
- 添加自行车,**删除自行车,**添加汽车 (1)
- 添加自行车,**删除自行车,**添加自行车 (1)
- 添加自行车, 添加自行车, 删除自行车, 删除自行车(1)
- 添加自行车,添加汽车,删除自行车,删除汽车 (1)
- 添加自行车,添加汽车**,删除汽车,删除**自行车 (1)
- 加车,加车, 删车, 删车 (1)
前四行涵盖了两两配对,其他的只是一个(加粗)。你可以看到,满足标准的测试集可能不包括测试的第一步(加车、加车、加自行车)。
因此,即使是T1的第一步也会丢失。即使是更强的标准也不会发现T1所检测到的错误。
然而,该方法的优点是可以普遍使用,如果状态不相关(见下几章),那么可以有效地使用。这就是大多数基于模型的测试工具(CA Agile Requirements Designer,Eggplant,Perfecto,Curiosity)应用这种技术的原因。
有状态的基于模型的测试
让我们在Google上搜索一下 "状态转换测试 "吧!所有的例子都包含有数量非常有限的状态的系统,比如ATM认证,设置时间和日期,以及开关灯。在实践中,(程序)状态的数量是巨大的,不能用于状态转换测试,导致数百万的测试案例。唯一的解决办法是减少状态的数量。如果我们只考虑 "内部状态 "和防护条件,就可以做到这一点。
例如,考虑到我们的需求规范,程序状态涉及自行车和汽车的数量以及一些内部状态。在这种情况下,图中有几个状态/节点,导致太多的测试案例。我们可以减少状态的数量,但如何减少呢?一个适当的解决方案是只考虑内部或测试状态。
在我们的例子中,我们有五个内部/测试状态。
- 没有折扣,没有自行车。
- 没有折扣,包括自行车。
- 折扣,添加自行车。
- 折扣,自行车转换。
- 停止。
这样做的好处是显而易见的:由于测试集的最低要求是覆盖每个状态,折扣将被测试。状态转换图就在这里。

同样,让我们假设测试选择标准是所有过渡对。让我们再次考虑我们的测试案例。
T:
- 增加一些车辆以增加一辆免费自行车。
- 删除一些车辆,直到自行车被撤走。
- 再次增加一些车辆,看免费自行车是否被归还。
第一步是通过达到状态Discount, bike added来完成的。从这里开始,有一个删除汽车的过渡,应该被穿越。这样,第二步就完成了。由于所有相邻的过渡对都应该被覆盖,我们应该选择回到状态Discount, bike added的add car。这意味着为了满足我们的标准,应该增加测试T,请看下面强调的这一对。

路径被生成。例如,从起点开始,遍历添加自行车到状态Discount,自行车转换是无效的,因为之前'添加汽车'应该遍历两次。因此,需要有防护条件。
例如,添加汽车过渡开始并回到无折扣、无自行车状态的防护条件是。
总价<400。
然而,总价是输出的,因此你应该根据要求来编码。
发生了什么?你建立了一个应用程序的模型,计算购物车中物品的总价。然而,在制作模型时,你应该对总价进行编码,这是实现的任务。很明显,你在编写这段代码时可能会犯错,测试也可能会出现错误。这是一个简单的例子,在有些情况下,对输出进行编码会更加困难。当有许多转换时,添加必要的防护条件是很耗时和容易出错的。
如果保护条件只包含输入,那么图将不包含输出值,因为在一个状态下,它可以根据穿越的路径而不同。当测试被生成时,你应该为每个测试案例添加正确的输出。
另一个问题是,当系统中没有内部状态时,如何处理这些状态?这并不容易,因为你应该临时切割状态,不知道基于减少的图的测试是否仍然可靠。我认为在这种情况下,无状态的解决方案更简单,而且考虑到缺陷检测,会导致同样的结果。
这可能是状态转换测试没有在测试人员中广泛使用的主要原因,而且实现它的工具少得多。这样的工具是Opkey和Conformiq Creator。
总结
我们回顾了两类基于模型的测试。最简单和最广泛使用的技术是无状态解决方案。这些工具可以普遍应用,而且易于使用,但有两个缺点。
- 它们需要约束/编码。
- 对于包括内部状态的复杂系统,它们只能检测到简单的错误。
状态转换测试方法更难使用,但更可靠,也就是说,对于更复杂的系统,它可以检测出棘手的bug。这些方法也有两个不足之处。
- 它们需要编码,有时应该对预期输出进行编码。
- 对于不太复杂的系统,确定必要的状态很困难,状态转换图也是如此
一个明显的问题是我应该选择哪一个。答案是它们都不是。第三类,"聚合 "是更好的。一种被称为动作状态测试的特定MBT方法解决了这些方法的所有问题。在我的下一篇博客中,我将展示这种技术和它的用法。