在基于类的测试中使用参数
通常,您需要运行一系列仅在测试数据方面不同的测试。例如,您可能要测试函数为不同输入产生预期的输出。在这种情况下,测试逻辑是相同的,各次测试之间的唯一区别是每次函数调用的实际值和预期值。通过参数化测试,您可以实现代码来使用不同数据值以迭代方式运行测试。当方法参数化后,测试框架会自动为每个参数值调用该方法。因此,您不需要为每个值实现单独的方法。
测试框架使您能够在不同级别参数化您的测试类。此外,当您使用多个参数调用一个测试类方法时,您可以指定如何为不同参数组合调用该方法。
如何编写参数化测试
从 matlab.unittest.TestCase
类派生的类可以使用框架特定的属性和方法特性来实现测试的参数化。要在基于类的测试中以参数形式提供数据,请使用 properties
块为这些数据指定适当的参数化特性。然后,将参数化属性作为输入参数传递给一个或多个方法。
例如,假设有一个 SampleTest
类。该类定义了一个参数化测试,因为它在 properties
块中指定具有 TestParameter
特性的属性。这些属性传递给 Test
方法,并用于执行验证。
classdef SampleTest < matlab.unittest.TestCase properties (TestParameter) numericArray = {int16(1),single(zeros(1,4)),magic(3)}; functionHandle = {@false,@() size([])}; end methods (Test) function test1(testCase,numericArray) testCase.verifyNotEmpty(numericArray) end function test2(testCase,functionHandle) testCase.verifyWarningFree(functionHandle) end end end
测试类生成一个包含五个元素的参数化测试套件。
suite = testsuite("SampleTest");
{suite.Name}'
ans = 5×1 cell array {'SampleTest/test1(numericArray=int16_1)' } {'SampleTest/test1(numericArray=1x4_single)' } {'SampleTest/test1(numericArray=3x3_double)' } {'SampleTest/test2(functionHandle=@false)' } {'SampleTest/test2(functionHandle=function_handle)'}
赋给参数化属性的值必须为非空元胞数组或有至少一个字段的标量结构体。测试框架使用属性值来指定测试套件中的参数名称和值:
如果属性值是元胞数组,则框架根据元胞数组元素的值、类型和维度,基于元胞数组的元素生成描述性参数名称。
如果属性值是结构体,则结构体字段表示参数名称,结构体值表示参数值。要完全控制套件中的参数名称,请使用结构体而不是元胞数组来定义参数。
如何初始化参数化属性
定义参数化属性时,必须初始化属性,以便 MATLAB® 可以生成参数名称和值。您可以在测试类加载时或测试套件创建时初始化属性:
类加载时:如果属性值可以在 MATLAB 加载测试类定义时确定,请使用默认值初始化属性。您可以在
properties
代码块中或在classdef
文件中使用局部函数来指定默认值。有关在类定义中赋值的详细信息,请参阅Evaluation of Expressions in Class Definitions。当在类加载时初始化参数化属性时,与属性相关联的参数对于不同的测试运行保持固定。每次从参数化测试类创建套件时,框架都使用相同的参数名称和值来运行测试。有关使用参数化属性和默认值的示例,请参阅创建基本参数化测试。
套件创建时:如果无法或不想在类加载时确定参数,请在套件创建时使用具有
TestParameterDefinition
属性的静态方法初始化属性。当您使用TestParameterDefinition
方法初始化参数化属性时,与该属性相关联的参数可能因不同测试运行而异。每次从参数化测试类创建套件时,框架都会生成新的参数名称和值来运行测试。有关详细信息,请参阅Define Parameters at Suite Creation Time。
一旦为参数化属性赋值,就不要修改它。例如,当使用默认值初始化参数化属性时,您无法使用 TestParameterDefinition
方法覆盖默认值。
一个参数可能被若干单元测试使用。使用相同参数的测试必须独立运行,不能无意间相互影响。此外,运行测试不能影响同一测试的后续重新运行。为了确保测试运行的独立性,请用值对象初始化参数化属性。不推荐使用句柄对象(如 MATLAB 图形对象)作为参数值。有关值和句柄对象行为的详细信息,请参阅句柄类和值类的比较。
如果您需要在参数化测试中测试句柄对象,请考虑通过使用函数句柄作为参数值并在测试中调用这些函数句柄来间接构造它们。例如,编写一个参数化测试来测试用 figure
和 uifigure
函数创建的图窗的默认当前点。
classdef FigureTest < matlab.unittest.TestCase properties (TestParameter) figureType = {@figure,@uifigure}; end methods (Test) function defaultCurrentPoint(testCase,figureType) fig = figureType(); testCase.addTeardown(@close,fig) cp = fig.CurrentPoint; testCase.verifyEqual(cp,[0 0]) end end end
指定参数化级别
您可以在类设置、方法设置和测试等三个级别参数化测试类。每个级别的参数化都要求参数化属性具有特定的属性特性。例如,在最高级别,可以使用在 properties
代码块中定义的具有 ClassSetupParameter
特性的属性来参数化 TestClassSetup
方法。在最低级别,可以使用在 properties
代码块中定义的具有 TestParameter
特性的属性来参数化 Test
方法。
下表显示不同参数化级别以及每个级别所需的方法和属性特性。
参数化级别 | 参数化定义 | 可访问的参数化属性 | |
---|---|---|---|
方法属性 | 属性特性 | ||
类设置级别 | TestClassSetup | ClassSetupParameter | ClassSetupParameter |
方法设置级别 | TestMethodSetup | MethodSetupParameter | MethodSetupParameter 和 ClassSetupParameter |
测试级别 | Test | TestParameter | TestParameter 、MethodSetupParameter 和 ClassSetupParameter |
参数化方法可以访问参数化属性,具体取决于方法在哪个级别上定义:
参数化
TestClassSetup
方法只能访问具有ClassSetupParameter
特性的参数化属性。参数化
TestMethodSetup
方法只能访问具有MethodSetupParameter
或ClassSetupParameter
特性的参数化属性。参数化
Test
方法可以访问任何参数化属性。
有关如何在不同级别参数化测试类的示例,请参阅创建高级参数化测试。
指定参数的组合方式
向一个方法传递多个参数化属性时,可以使用 ParameterCombination
方法特性来指定参数的组合方式。测试框架对指定的组合调用方法。
下表显示不同参数组合策略。
ParameterCombination 属性值 | 方法调用 | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
"exhaustive" (默认值) | 对所有的参数值组合调用方法。如果您不指定 | |||||||||||||||||||||
"sequential" | 使用对应的参数值调用方法。参数化属性必须指定相同数量的参数值。例如,如果对一个方法提供了两个参数化属性,并且每个属性指定三个参数值,则框架调用该方法三次。 | |||||||||||||||||||||
"pairwise" | 至少对每对参数值调用一次测试方法。与 例如,此测试使用 classdef ZerosTest < matlab.unittest.TestCase properties (TestParameter) rowCount = struct("r1",1,"r2",2,"r3",3); columnCount = struct("c1",2,"c2",3,"c3",4); type = {'single','double','uint16'}; end methods (Test,ParameterCombination="pairwise") function testSize(testCase,rowCount,columnCount,type) testCase.verifySize(zeros(rowCount,columnCount,type), ... [rowCount columnCount]) end end end 根据类创建一个测试套件。一个可能的结果包含下面列出的十个元素。 suite = testsuite("ZerosTest");
{suite.Name}' ans = 10×1 cell array {'ZerosTest/testSize(rowCount=r1,columnCount=c1,type=single)'} {'ZerosTest/testSize(rowCount=r1,columnCount=c2,type=double)'} {'ZerosTest/testSize(rowCount=r1,columnCount=c3,type=uint16)'} {'ZerosTest/testSize(rowCount=r2,columnCount=c1,type=double)'} {'ZerosTest/testSize(rowCount=r2,columnCount=c2,type=single)'} {'ZerosTest/testSize(rowCount=r2,columnCount=c3,type=single)'} {'ZerosTest/testSize(rowCount=r3,columnCount=c1,type=uint16)'} {'ZerosTest/testSize(rowCount=r3,columnCount=c2,type=single)'} {'ZerosTest/testSize(rowCount=r3,columnCount=c3,type=double)'} {'ZerosTest/testSize(rowCount=r2,columnCount=c2,type=uint16)'} 测试框架保证对于由任意两个属性指定的参数值的每个组合都调用
虽然框架可保证至少为每对值创建一次测试,但您不应依赖套件大小、顺序或特定的测试套件元素集。 | |||||||||||||||||||||
" | 至少对每个参数值的 您可以互换使用 |
您可以在类设置、方法设置和测试级别组合参数。例如,使用两个方法特性 TestMethodSetup,ParameterCombination="sequential"
来指定在 properties
代码块中定义的、具有 MethodSetupParameter
特性的方法设置级别参数的顺序组合。
在测试中使用外部参数
创建参数化测试时,可以通过将输入注入基于类的测试中来重新定义参数。要提供在测试文件之外定义的数据,请创建一个 Parameter
实例,并在创建测试套件时使用 ExternalParameter
名称-值参数。有关详细信息,请参阅在参数化测试中使用外部参数。
另请参阅
matlab.unittest.parameters
| matlab.unittest.parameters.Parameter
| matlab.unittest.TestCase