本主题说明如何在测试期间使用预定义或自动生成的数据源值。针对不同数据集运行测试能够让您轻松增加测试量和覆盖率。
章节目录:
在桩函数中使用数据源 您配置的在 C++test 中使用的任何数据源都可以在桩函数中使用。详细信息请参阅在桩函数中使用数据源。 |
可以使用 GUI 向导在 C++test 中定义数据源。在向导中,通过逗号分隔值文件(.csv)、Excel 电子表格(.xls)、数据库查询或 C++test 管理的数据源表指定数据源。在测试用例执行过程中,测试用例会使用从特定数据源收集的值。
托管数据源可以通过测试用例浏览器在项目级别(对所有项目测试套件可见)或在单个测试套件级别(仅由特定的测试套件使用)定义。此外,还可以在工作空间级别创建托管数据源)。
在项目/测试套件级别创建托管数据源的步骤:
指定数据源类型。
配置数据源 (例如,指定 .csv 文件的路径,或数据库参数和 SQL 查询)。
指定 JDBC 驱动程序 连接到数据库需要 JDBC 驱动程序。您可以通过首选项面板的 JDBC 驱动程序页面添加驱动程序(Parasoft> 首选项> JDBC 驱动程序)。 |
在 工作空间 级别创建托管数据源的步骤:
C++test 可以生成逗号分隔值文件(.csv)或包含自动生成的数据源值的 C++test 管理表数据源。
有关在测试用例编辑器中自动生成新数据源的信息,请参阅使用数据源参数化在测试用例编辑器中添加的测试用例。
如果在测试用例向导中配置测试用例:
生成数据源时,C++test 为每个具有可编辑值(布尔、整数、浮点和字符串类型)的前置和后置条件创建一个单独的列。该数据源在测试套件级别生成。
生成后,生成的数据源就可以用于参数化测试套件中的其他测试用例。
您可以使用数据源值来参数化通过测试用例编辑器创建的测试用例(请参阅使用测试用例编辑器添加测试套件和测试用例)。这些值可以在内置到测试用例中的数据源表中提供,也可以在外部数据源中提供。
您可以使用数据源值来参数化已有的(自动生成的或用户定义的)测试用例,以及在测试用例向导中编写测试用例时定义测试用例值。数据源值可以取自上文描述的 C++test 中定义的任何数据源。
C++test 无法自动验证由数据源值参数化的测试用例执行产生的未验证的结果和失败的断言。这是因为它通常也要求使用通过数据源参数化的期望结果。 |
使用测试用例向导添加从先前定义的托管数据源中提取值的新测试用例:
使用先前定义的托管数据源参数化已有(自动生成或用户定义)测试用例:
例如,要使用 Excel 工作表中的值参数化已有测试用例,我们之后将该工作表称为“MyDataSourceForSum”:
找到测试用例注册行(在测试套件文件中)。例如:
... CPPTEST_TEST(sumTest1); ... |
将注册类型更改为使用数据源(your_Data_Source_name):
... CPPTEST_TEST_DS(sumTest1, CPPTEST_DS("MyDataSourceForSum")); ... |
... /* CPPTEST_TEST_CASE_BEGIN sumTest1 */ void MyTestSuite::sumTest1() { int iVal = CPPTEST_DS_GET_INTEGER("i"); int jVal = CPPTEST_DS_GET_INTEGER("j"); int expectedResult = CPPTEST_DS_GET_INTEGER("result"); CPPTEST_ASSERT_INTEGER_EQUAL(expectedResult, sum(iVal, jVal)); } /* CPPTEST_TEST_CASE_END sumTest1 */ ... |
执行测试用例时,它将使用来自“MyDataSourceForSum”Excel 类型数据源的值进行参数化。
以下宏可用于访问数据源中的值。每个宏都采用参数 NAME,该参数指定了数据源列的唯一标识符。
目的... | 使用此宏... | 注意 |
---|---|---|
返回以 null 结尾的字符串值 |
| N/A |
返回一个字符值 |
| N/A |
返回一个整数值 |
| N/A |
返回一个无符号整数值 | unsigned long long CPPTEST_DS_GET_UINTEGER(const char* NAME) | N/A |
返回一个浮点值 | long double CPPTEST_DS_GET_FLOAT(const char* NAME) | N/A |
返回一个布尔值 | int CPPTEST_DS_GET_BOOL(const char* NAME) | N/A |
返回内存缓冲区 | const char* CPPTEST_DS_GET_MEM_BUFFER (const char* NAME, unsigned int* SIZE_PTR) | 如果 SIZE_PTR 不为空,则缓冲区的大小将存储在相应位置。 |
返回一个枚举值 |
| <scoped enum name> 是枚举的全名,包括所有命名空间名称。例如: INNER_NS::MyEnumeration, INNER_NS::MyClass::MyEnumeration |
从 SOURCE 数组返回值 | CPPTEST_DS_GET_VALUE(SOURCE) | 应将数组类型的变量指定为 SOURCE 参数: 每次执行测试用例后,自动提取值的行号会自动增加。 |
返回当前迭代(行号) | unsigned int CPPTEST_DS_GET_ITERATION( ) | 可用于确定当前迭代/行号。 |
如果 'name' 列存在于当前数据源的当前迭代中,则返回非零值。 | int CPPTEST_DS_HAS_COLUMN(const char* name) | 可用于测试用例特定行为的桩函数(请参阅在桩函数中使用数据源)。如果不使用数据源,则返回零。 |
下表提供了特定类型数据源值的支持格式相关的信息:
数据源类型 | C++test API 函数 | 支持的格式 | |
---|---|---|---|
布尔 | CPPTEST_DS_GET_BOOLEAN() | 如果为 true:
如果为 false:
| |
有符号整数 | CPPTEST_DS_GET_INTEGER() | 整数的十进制表示法(可以使用- 或 + 号作为前缀);例如:
前缀为 0x 或 0X 的整数十六进制表示法(可以使用- 或 + 号作为前缀);例如:
前缀为 0 的整数八进制表示法(可以使用- 或 + 号作为前缀);例如:
前缀为 0b 或 0B 的整数二进制表示法(可以使用- 或 + 号作为前缀);例如:
| |
无符号整数 | CPPTEST_DS_GET_UINTEGER() | 整数的十进制表示法;例如:
前缀为 0x 或 0X 的整数十六进制表示法;例如:
前缀为 0 的整数八进制表示法;例如:
前缀为 0b 或 0B 的整数二进制表示法;例如:
| |
浮点 | CPPTEST_DS_GET_FLOAT() | 浮点值的实际格式取决于所使用的编译器的配置——CPPTEST_STR_TO_FLOAT / CPPTEST_SCANF_FLOAT / 此外,后面也可以跟一个十进制指数。十进制指数由“E”或“e”组成,后跟可选的加号或减号,然后是一串非空的十进制数字,表示乘以 10 的幂。 示例:
| |
char | CPPTEST_DS_GET_CHAR() | 使用的字符;例如:
此外,应使用以下类似 C 语言风格的转义序列来处理特殊字符:
对于不可打印的字符,可以使用字符的八进制(1-3 个前缀为 \ 的数字)或十六进制(1-2 个前缀为 \x 的数字)表示法;例如:
| |
string | CPPTEST_DS_GET_CSTR() | 要使用的字符串;例如:
此外,应使用以下类似 C 语言风格的转义序列来处理特殊字符:
示例:
要在字符串中使用不可打印的字符,可以使用字符的八进制(1-3 个前缀为 \ 的数字)或十六进制(1-2 个前缀为 \x 的数字)表示法;例如:
为了将空值定义为数据源中的 C 字符串值,可以配置托管数据源识别一些字符;例如 $ 作为特殊值前缀,可以在数据源中使用带有该前缀字符的 NULL;例如:
请注意,这只能用于 UI 管理的数据源(用 CPPTEST_DS("ds_name") 宏注册的测试用例)。 | |
数据缓冲区 | CPPTEST_DS_GET_MEM_BUFFER() | 使用与字符串类型相同的格式(请参阅上文)。 | |
枚举值 | CPPTEST_DS_GET_ENUM() | 可以使用简单的枚举值名称或包含所有命名空间名称的完整名称。例如,如果在 ::INNER_NS::MyClass::DaysEnumeration 中定义 MON 枚举值,您可以输入 MON 或 ::INNER_NS::MyClass::MON。
|
下表提供了有关使用特定数据源类型格式化数据源值的信息:
数据源类型 | 数据源注册宏 | 其他信息 |
---|---|---|
CSV 文件(托管数据源) | CPPTEST_DS() | 数据源值可以用引号括起来(可以在数据源配置中定义引号);例如:
这对于需要在值中使用分隔符或者引号本身的情况而言是必需的。针对后面一种情况,需要将用作数据源值中元素的引号重复使用;例如:
|
Excel 电子表格(托管数据源) | CPPTEST_DS() | 不需要进行特定的 Excel 值准备。注意,需要格式化数据以用于上述特定类型。 为了确保 C++test 正确处理值,您可以对电子表格中的所有单元格使用文本格式。例如,通过这种方式,您可以确保整数值的八进制表示形式被正确处理。 |
表格(托管数据源) | CPPTEST_DS() | 不需要进行特定的表格值准备。注意,需要格式化数据以用于上述特定类型。 |
CSV file | CPPTEST_DS_CSV() | 数据源值可以用引号括起来(可以在数据源配置中定义引号);例如:
这对于需要在值中使用分隔符或者引号本身的情况而言是必需的。针对后面一种情况,需要将用作数据源值中元素的引号重复使用;例如:
|
源代码数组 | CPPTEST_DS_ARRA Y() | 不需要进行数组特定的值准备。注意,需要格式化数据以用于上述特定类型。 |
数据库(托管数据源) | CPPTEST_DS() | 不需要进行特定的数据源值准备。注意,需要格式化数据以用于上述特定类型。 |
查看或编辑现有数据源配置的步骤:
然后,您可以在打开的窗口中查看数据源配置,并根据需要进行修改。
打开数据源文件的步骤:
有关在项目/测试套件级别添加的数据源的信息存储在每个项目内部的 .parasoft 属性文件中。因此,您可以通过源码控制(提交和签出 .parasoft 属性文件)共享有关数据源的信息。
全局数据源——在工作空间级别添加的数据源——也可以通过这种方式共享,但是您需要定义在哪里(例如,在哪个项目中)创建额外的包含全局数据源信息的 .datasource 数据文件。默认情况下,全局数据源信息存储在本地。
为全局数据源指定共享位置的步骤:
数据源便携模式旨在应对需要将测试套件文件移动到不同位置的特定开发环境。在标准数据源模式下,如果将测试套件文件移动到其他位置,该数据源关联将会丢失。而在便携模式下,您可以轻松将数据源与测试套件一起移动。
为了确保数据源便携性,所有测试套件级别的数据源信息都存储在与测试套件文件名同名且扩展名为 .properties 的文件中。因此,每个测试套件都有自己的数据源设置文件,并且可以与这些数据一起移动。
便携式数据源模式仅适用于在测试套件级别创建的数据源,不适用于在项目或工作空间级别创建的数据源。
更改数据源设置的步骤:
右键点击项目节点,从快捷菜单选择 属性。
每次切换模式时,与测试套件关联的所有现有数据源都会自动转换为所选模式。切换到便携模式,数据源设置将从 .parasoft 文件传输到相应的 <test_suite_name>.properties 文件。转换完成后,您会收到消息通知。
您可以通过提交和签出所有或选定的 *.properties 文件,通过源码控制共享有关数据源的信息。
为确保 Excel 和 CSV 数据源类型具有更好的便携性,您可以在 Excel/CSV 文件路径中使用 C++test ${test_suite_loc} 变量。该变量解析为测试套件文件位置的绝对路径。
除了使用托管数据源(如上所述,在 C++test 中定义的数据源)之外,您还可以使用未作为托管数据源添加的数组或 CSV 文件中的值。
C++test 中数据源的一种实现方式是基于类型化数组,这些数组随后被单个测试用来逐行遍历数据。
创建可以访问数组数据源的测试用例的步骤:
CPPTEST_TEST_DS(testCaseName, dataSourceCon-fig)
宏注册测试用例。将 testCaseName
替换为要访问数据源值的测试用例函数的名称,并将 dataSourceConfig
替换为用于数据源配置的宏。
dataSourceConfig
可使用两个辅助宏:CPPTEST_DS_ARRAY(ARRAY_NAME, ROW, COLUMN)
和 CPPTEST_DS_REPEAT(NUMBER)。
对于 CPPTEST_DS_ARRAY(ARRAY_NAME, ROW, COLUMN)
,需注意:
const char* data[ROW][COLUMN]。
对于 CPPTEST_DS_REPEAT(NUMBER)
,需注意:
提示
#include "cpptest.h" int plus_one(int); class TestSuite_3 : public CppTest_TestSuite { public: CPPTEST_TEST_SUITE(TestSuite_3); CPPTEST_TEST_DS(test_ds_repeat, CPPTEST_DS_REPEAT(2)); CPPTEST_TEST_SUITE_END(); void setUp(); void tearDown(); void test_ds_repeat(); private: int _dsRepeat_arg[2]; int _dsRepeat_return[2]; }; CPPTEST_TEST_SUITE_REGISTRATION(TestSuite_3); void TestSuite_3::setUp() { _dsRepeat_arg[0] = 1; _dsRepeat_arg[1] = 2; } void TestSuite_3::tearDown() { } void TestSuite_3::test_ds_repeat() { int index = CPPTEST_DS_GET_ITERATION() - 1; int _value = _dsRepeat_arg[index]; int _expected = _dsRepeat_return[index]; int _return = plus_one(_value); CPPTEST_ASSERT_INTEGER_EQUAL(_expected, _return); } |
除了支持前面章节目录描述的 CSV 数据源之外,当 CSV 数据满足以下条件时,C++test 还可以使用“非托管”CSV 数据源:
有关支持文件类型的更多信息,请参阅 CSV 文件支持详细信息。
创建可以访问 CSV 数据源的测试用例的步骤:
CPPTEST_TEST_DS(testCaseName, dataSourceConfig)
宏注册测试用例。将 testCaseName
替换为要访问数据源值的测试用例函数的名称,并将 dataSourceConfig
替换为 CPPTEST_DS_CSV(FILE_NAME, SEPARATOR, HAS_HEADER, TRIM)。
CPPTEST_DS_CSV 定义在 C++test 数据源 API 中。其参数为:
如果 CSV 文件中没有表头,那么每个字段名就是它的序号(从 0 开始)。值需要使用引号(字符串类型)。例如: CPPTEST_DS_GET_FLOAT("0");
FILE_NAME 可以是完整或相对文件路径。后者是相对于当前目录的路径。
CPPTEST_DS_CSV("/home/project/testdata.csv", ',', 1, 0)
CPPTEST_DS_CSV("testdata.csv", ';', 0, 1)
在本例中,使用了一个数组数据源。首先,定义数据数组:
const char* MyData[][3] = { { "arg1", "arg2", "return" }, { "1", "one", "111" }, { "0", "two", "222" }, { "2", "three", "333" }, }; |
它包括三列四行。第一行包含列的名称(arg1
、arg2
和 return
)。另外三行保存测试用例的数据(因此将有三个测试用例执行)。准备好数据后,必须注册测试用例,方法如下:
CPPTEST_TEST_DS(testFoo2, CPPTEST_DS_ARRAY(MyData, 4, 3)); |
测试用例 testFoo2
将执行三次,并使用来自 MyData
数组的数据。最后,我们需要编写测试用例。被测函数接受两个参数并返回一个整数。我们将从数据源中获取这两个参数:
int arg1 = CPPTEST_DS_GET_INTEGER("arg1"); const char * arg2 = CPPTEST_DS_GET_CSTR("arg2"); |
如果具有指定名称的列不可用或其值类型不匹配,测试用例执行将会中止。
对于后置条件检查,我们将从数据源中获取期望的值:
int expectedReturn = CPPTEST_DS_GET_INTEGER("return"); /* check return value with value from data source */ CPPTEST_ASSERT_INTEGER_EQUAL(expectedReturn, actualReturn); |
以下是完成的测试:
class TestWithDataSource : public CppTest_TestSuite { public: CPPTEST_TEST_SUITE(TestWithDataSource); /* register standard test case */ CPPTEST_TEST(testFoo1); /* register test case with "MyData" data source access */ CPPTEST_TEST_DS(testFoo2, CPPTEST_DS_ARRAY(MyData, 4, 3)); CPPTEST_TEST_SUITE_END(); ... ... ... /* test case using values from data source */ void TestWithDataSource::testFoo2() { /* extract arguments from data source */ int arg1 = CPPTEST_DS_GET_INTEGER("arg1"); const char * arg2 = CPPTEST_DS_GET_CSTR("arg2"); /* call tested function */ int actualReturn = foo(arg1, arg2); /* extract expected return from data source */ int expectedReturn = CPPTEST_DS_GET_INTEGER("return"); /* check return value with value from data source */ CPPTEST_ASSERT_INTEGER_EQUAL(expectedReturn, actualReturn); } |
在本例中,我们将使用重复数据源:
/* data source for arg1 */ int _arg1[] = { 1, 0, 2, }; /* data source for arg2 */ const char * _arg2[] = { "one", "two", "three", }; /* data source for return */ int _return[] = { 111, 222, 333, }; |
这是一个特殊的数据源:它不提供对任何数据的访问,只是根据请求次数重复测试用例执行。用户指定在每次运行期间应该使用什么数据。在本例中,我们使用三个独立的数组:_arg1
、_arg2
和 _return
。
注册测试用例:
CPPTEST_TEST_DS(testFoo2, CPPTEST_DS_REPEAT(3)); |
CPPTEST_DS_REPEAT
参数指示测试用例 testFoo2
应该执行几次。
在该测试用例中,我们将直接从数组中获取数据。要从正确的行中获取值,我们可以使用 CPPTEST_DS_GET_ITERATION()
的值作为数组索引,或使用 CPPTEST_DS_GET_VALUE(SOURCE)
宏为执行索引:
int arg1 = CPPTEST_DS_GET_VALUE(_arg1); const char * arg2 = CPPTEST_DS_GET_VALUE(_arg2); |
CPPTEST_DS_GET_VALUE
宏中没有范围或类型检查。它只是一个索引(假设数组有正确的索引)。
以下是完成的测试:
class TestWithDataSource : public CppTest_TestSuite { public: CPPTEST_TEST_SUITE(TestWithDataSource); /* register standard test case */ CPPTEST_TEST(testFoo1); /* register test case that will be executed number of times */ CPPTEST_TEST_DS(testFoo2, CPPTEST_DS_REPEAT(3)); CPPTEST_TEST_SUITE_END(); ... ... ... /* test case using values from custom arrays */ void TestWithDataSource::testFoo2() { /* extract arguments from data source */ int arg1 = CPPTEST_DS_GET_VALUE(_arg1); const char * arg2 = CPPTEST_DS_GET_VALUE(_arg2); /* call tested function */ int actualReturn = foo(arg1, arg2); /* extract expected return from data source */ int expectedReturn = CPPTEST_DS_GET_VALUE(_return); /* check return value with value from data source */ CPPTEST_ASSERT_INTEGER_EQUAL(expectedReturn, actualReturn); } |
下面是存储在 /home/test/t2.data
文件中数据的数据源声明,其中表头和字段用逗号(',')分隔。不会去除空格。
class TestSuite : public CppTest_TestSuite { public: CPPTEST_TEST_SUITE(TestSuite); CPPTEST_TEST_DS(test_fooc_1, CPPTEST_DS_CSV("/home/test/t2.data",',', 1, 0)); CPPTEST_TEST_SUITE_END(); void setUp(); void tearDown(); void test_fooc_1(); }; |
下面是使用数据源 API 中标准函数的测试用例的定义:
void TestSuite::test_fooc_1() { int _boo = CPPTEST_DS_GET_INTEGER("first"); float _goo = CPPTEST_DS_GET_FLOAT("second"); int _return = fooc(_boo, _goo, _g); CPPTEST_ASSERT_INTEGER_EQUAL(CPPTEST_DS_GET_INTEGER("result"), ( _return )) } |
以下是 CSV 数据源文件(/home/test/t2.data
),包含一个表头和三个测试用例。
first,second,result 1,2,3 2,2,4 2,2,5 |
为 C++test 随附的 ATM 示例项目(examples 目录中)创建两个基于数据源的回归测试(一个使用字符串数组数据,另一个使用类型化数组数据):
右键点击项目节点,然后 选择新建> 其他,选择 C++test> 测试套件,然后点击下一步。将打开一个新的测试套件向导。
TestSuiteAccount
(这将是 C++test 套件的类名),将测试套件位置指定为 /ATM/tests/user
(如果目录不存在,点击创建目录)。setPasswordTest
,另一个命名为 depositTest
。完成向导设置将创建一个框架测试套件,并注册了相应测试。创建一个用于测试密码设置的字符串数据源数组(如下所示),并将其放在测试套件的源文件中,位于测试套件类声明的上方。通常而言,数据源数组可以遵循标准语言习惯在文件范围内声明,或者作为测试套件类的静态成员声明:
const char* passwordData [] [2] = { { "arg1", "result" }, { "", "" }, { "a1", "a1" }, { "really_long_password", "really_long_password" }, { "foo", "goo" } }; |
为 setPassword 方法编写一个测试用例。该测试应设置密码并检查返回的密码是否符合数据源中指定的密码:
void TestSuiteAccount::setPasswordTest() { const char* password = CPPTEST_DS_GET_CSTR("arg1"); const char* result = CPPTEST_DS_GET_CSTR("result"); Account _cpptest_object; _cpptest_object.setPassword(password); CPPTEST_ASSERT_CSTR_EQUAL(result, _cpptest_object.getPassword()) } |
#include “Account.hxx”
添加到文件顶部,这样我们就可以获得 Account 类声明。按如下方式修改测试套件声明 CPPTEST_TEST(setPasswordTest)
中的测试用例注册信息:
CPPTEST_TEST_DS(setPasswordTest, CPPTEST_DS_ARRAY(passwordData, 5, 2)); |
/tests/user/*
中的测试。使用类型化数组中的数据创建第二个测试用例。创建一个包含 deposit 值的 double 类型数据源数组,以及另一个包含期望的帐户 balance 值的数据源数组。这一次,我们让数组成为测试套件的成员。测试套件声明的结尾应类似于下面的示例:
void depositTest(); private: static double deposit []; static double balance []; }; double TestSuiteAccount::deposit [] = { 0.0, 1.0, -2.0, 1e06 }; double TestSuiteAccount::balance [] = { 1.0, 2.0, -1.0, 1000001.0 }; |
为 deposit 函数编写一个测试,并检查最终 balance 以进行回归:
void TestSuiteAccount::depositTest() { double dep_value = CPPTEST_DS_GET_VALUE(deposit); double balance_value = CPPTEST_DS_GET_VALUE(balance); Account _cpptest_object(1.0); // create account with initial deposit _cpptest_object.deposit(dep_value); // additional deposit CPPTEST_ASSERT_DOUBLES_EQUAL(balance_value, _cpptest_object.getBal-ance(), 1e-6) } |
将测试 CPPTEST_TEST(depositTest) 的注册信息更改为运行 4 次:
CPPTEST_TEST_DS(depositTest, CPPTEST_DS_REPEAT(4)); |