热门IT资讯网

C++组件测试及应用 — 基于Tessy的测试技术漫谈

发表于:2024-11-22 作者:热门IT资讯网编辑
编辑最后更新 2024年11月22日,编者按:随着行业软件的安全性关注度递增,在产品测试初期开展全面单元/集成测试逐渐成为了开发测试过程中关键环节,它将有助于尽早发现隐患,降低开发成本以及更加充分的功能验证。ISO26262及ASPICE

编者按:随着行业软件的安全性关注度递增,在产品测试初期开展全面单元/集成测试逐渐成为了开发测试过程中关键环节,它将有助于尽早发现隐患,降低开发成本以及更加充分的功能验证。ISO26262及ASPICE中同样对单元/集成测试的实施提出了相应的要求。针对代码的单元测试,要求对程序中每个独立单元开展功能测试,而几个层次结构的单元组合,如果有明确的功能,也可以把该单元组合称为模块。对于集成测试,要求针对具备完整功能的模块进行测试,此处具有完整功能的模块,我们可以称为组件,并且通过增式测试的方式逐渐实现各组件的组合测试,因此,集成测试也可以看作是针对这些组件的功能和组件组合功能进行综合验证的过程。

此文章来源于Tessy原厂Hitex于11月底发布的白皮书《Component Testing of Test Objects in C++ --Writing scenarios for integration testing in TESSY》。

1 从单元到组件

1.1测试对象的类型

我们将基于测试对象类型讨论"单元测试"、"模块测试"、"集成测试"和"组件测试",侧重介绍时序的组件测试(假设软件是C或者C++开发)。

1.1.1独立单元

单个函数是C语言程序中最小的合理测试对象,通常被认为是一个单元。如果编程语言是C++,那么将方法视为一个单元。

单元测试是基于被测单元接口(即输入和输出)开展的功能验证。开展单元测试时,会实际编译执行被测程序,如果被测试的"单元"调用了其他函数,可以通过创建桩函数对调用的函数进行替换以保证测试的顺利进行,提高测试效率。

1.1.2 具有层次结构的单元

具有层次结构的多个单元可以以一种类似于单个单元测试的方式开展,将顶层单元作为测试对象,关注整体功能,被调用单元看作内部实现,针对整体的输入输出开展测试。


Fig.1 将顶层单元作为测试对象的单元层次结构


从技术角度,这可以通过不使用桩函数替换被调用单元实现。仍可以像上面那样将其视为单元测试,只不过是更大的单元。

这也可以看作是单元层次结构中的集成测试,因为从某种程度上,它们必须能正确地一起工作才能通过测试。这样的单元层次结构也能被称为模块(Module),但是我不想用这个术语,因为这可能会与C/C++程序的源模块概念混淆。(一个C源模块不能直接作为模块测试对象,因为它是依照语法定义的,而用于模块测试的module通常是按照语义定义的。) 多个单元层次结构的测试在技术上与单个单元测试非常类似,由于当前文档主题是组件测试,因此进一步讨论的是功能层次结构的测试。

1.1.3 相互作用且无时序关系的单元

与单元层次结构相反,在接下来,我们认为单元之间不一定具有调用关系,然而,我们假设这些单元相互协同工作,例如操作公共数据以实现一个共同目标。

众所周知的抽象数据类型"栈(stack)",及其push和pop操作就是一个很浅显易明的例子。pop和push操作的独立测试是单元测试,但是仍需要进行必要的集成测试。集成测试由一系列pop和push操作组成。该测试用例的输入由栈的初始内容和push操作的参数值组成,结果是pop操作的返回值和栈的最终状态。如果push和pop操作可能导致对单元的额外调用,例如对栈溢出/下溢的错误处理单元的调用,这些调用也属于集成测试用例的预期结果。


Fig.2 对于抽象数据类型"栈(stack)"的push和pop操作,单元测试是不充分的


"模块(Module)"这个术语可能更适合于这样一个协作单元集合,但我更喜欢用"组件(component)"这个术语,因为它的含义与C/C++源模块不一样。


Fig.3 组件的内部结构及其与外部的接口


"组件"内含的"单元"中,必须至少有一个具备从组件外部调用以驱动组件的功能。通常一个组件的几个单元来自外部调用,我们称这些单元为"组件函数"或"组件单元"。对一个组件的测试不再由(一个或多个)对单个单元的调用(如上面两部分所述)组成,而是由对(不同的)组件单元的一系列调用组成。对组件单元的调用将激活组件,与单个单元测试一样,组件的测试用例也包括输入和输出数据(组件的变量和被调用组件单元的参数)。组件可能具有内部单元,这些单元不能从组件外部调用,只能通过组件内部的函数调用获取。

内部单元所做的工作(如果有的话)与组件测试无关,因此组件测试中,是将整个组件看作一个黑盒。然而,与组件测试结果相关的是从组件内部到其他组件中(可调用的)单元的调用序列。这涉及到调用的数量、调用的顺序和调用传递给其他组件的参数。

很显然,组件中单元的功能以及它们之间的接口大部分情况下是通过组件测试来实现验证。因此,组件测试可以看作是对组件中单元的集成测试。



1.1.4 相互作用且有时序关系的单元

在上一节中,时序不是重点,无论是组件的激发调用时长,还是组件的激发调用与另一个组件的结果调用之间的时长。然而,这是一个检查收到激发调用后响应是否足够快的重要测试,即在定的时间范围内。

为了能够在模拟环境中测试组件的时序行为,需要有一个模拟的时间基数。这意味着组件内部的某个单元在已知的等距时间内被调用(例如每10ms),通常组件在实时操作系统(RTOS)的控制下执行并使用time-slicing(例如OSEK)就是这种情况,但一个简单的中断驱动应用程序通常也符合这种要求。

对该单元的调用代表组件的"心跳(heartbeat)",它们为测试组件的时序行为提供了一个(模拟的)时间参考。心跳函数通常被称为"handler function"或"work task"或简单称为"tick"。


Fig.4 如果心跳函数存在,则可以测试时序行为


2 C++栈的例子

2.1 介绍

正如前面所介绍的,抽象数据类型栈是一个典型的例子,在Tessy中进行集成测试需要使用Tessy的组件测试功能(在场景编辑器透视图中可以访问)来完成。而且,push和pop不相互调用,而是在公共数据(栈)上通信,因此,单纯的单元测试技术手段是不适用的,因为它们要求单元有一个单一的入口点,即单元测试侧重测试一个单一函数/方法。


Fig.5 stack.push和stack.pop不互相调用,需要集成测试


2.2 测试对象源代码


Fig.6 测试对象的源码(stack.cpp)


注:源代码最后三行不属于堆栈的实现,而是Tessy所要求的,由于使用了#ifdef,所以tick()函数只在源文件被Tessy处理时才出现。


2.3 Tessy准备工作

测试环境选择GCC(C++),测试类型选择Component,导入代码。


Fig.7 选择编译环境


2.4 接口设置

新建一个变量my_stack作为实例化的对象。


Fig.8 接口设置

2.5 测试用例设计和执行

创建第一个测试用例:验证入栈出栈操作。


Fig.9 场景1的设计和执行结果


创建第二个测试用例:该用例目的是验证栈下溢情况下的功能实现。

在Edit Test Execution Settings界面勾选Test Cases Separately分别执行两个场景,场景及结果如下图所示。


Fig.10 场景2的设计和执行结果


在SCE的用例设计界面中,针对指针的操作如下:为指针*arr创建一个对象,指定其为一个长度为4的数组;指向这个目标的指针由Tessy分配给对象my_stack中的变量arr,也就是说,分配的内存代替了由测试对象的构造函数分配的内存,由于此前将指向指针对象的接口设置为INOUT,所以该对象同时出现在Inputs和Outputs列项中。


Fig.11 为指针*arr创建一个对象


Fig.12 场景2 SCE执行实际结果


经纬恒润

北京市海淀区知春路7号致真大厦D座6层

电话:010-64840808

邮箱:[email protected]

网址:www.hirain.com


0