Main Content

本页翻译不是最新的。点击此处可查看最新英文版本。

在基于类的测试中使用参数

通常,您需要运行一系列仅在测试数据方面不同的测试。例如,您可能要测试函数为不同输入产生预期的输出。在这种情况下,测试逻辑是相同的,各次测试之间的唯一区别是每次函数调用的实际值和预期值。通过参数化测试,您可以实现代码来使用不同数据值以迭代方式运行测试。当方法参数化后,测试框架会自动为每个参数值调用该方法。因此,您不需要为每个值实现单独的方法。

测试框架使您能够在不同级别参数化您的测试类。此外,当您使用多个参数调用一个测试类方法时,您可以指定如何为不同参数组合调用该方法。

如何编写参数化测试

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 图形对象)作为参数值。有关值和句柄对象行为的详细信息,请参阅句柄类和值类的比较

如果您需要在参数化测试中测试句柄对象,请考虑通过使用函数句柄作为参数值并在测试中调用这些函数句柄来间接构造它们。例如,编写一个参数化测试来测试用 figureuifigure 函数创建的图窗的默认当前点。

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 方法。

下表显示不同参数化级别以及每个级别所需的方法和属性特性。

参数化级别参数化定义可访问的参数化属性
方法属性属性特性
类设置级别TestClassSetupClassSetupParameterClassSetupParameter
方法设置级别TestMethodSetupMethodSetupParameterMethodSetupParameterClassSetupParameter
测试级别TestTestParameterTestParameterMethodSetupParameterClassSetupParameter

参数化方法可以访问参数化属性,具体取决于方法在哪个级别上定义:

  • 参数化 TestClassSetup 方法只能访问具有 ClassSetupParameter 特性的参数化属性。

  • 参数化 TestMethodSetup 方法只能访问具有 MethodSetupParameterClassSetupParameter 特性的参数化属性。

  • 参数化 Test 方法可以访问任何参数化属性。

有关如何在不同级别参数化测试类的示例,请参阅创建高级参数化测试

指定参数的组合方式

向一个方法传递多个参数化属性时,可以使用 ParameterCombination 方法特性来指定参数的组合方式。测试框架对指定的组合调用方法。

下表显示不同参数组合策略。

ParameterCombination 属性值方法调用
"exhaustive"(默认值)

对所有的参数值组合调用方法。如果您不指定 ParameterCombination 特性,则测试框架使用此默认组合。

"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)'}

测试框架保证对于由任意两个属性指定的参数值的每个组合都调用 testSize 方法。例如,对于 rowCountcolumnCount 属性,下表显示对应于参数值组合的 TestSuite 数组索引。每个组合由至少一个 Test 元素表示。

columnCount 属性
c1c2c3
rowCount 属性r1123
r245、106
r3789

虽然框架可保证至少为每对值创建一次测试,但您不应依赖套件大小、顺序或特定的测试套件元素集。

"n-wise"

至少对每个参数值的 n 元组调用一次方法(需要 MATLAB Test™)。您可以将 n 指定为一个介于 2 和 10 之间的整数。例如,将属性指定为 "4-wise",可为每个四参数值组合至少调用一次方法。

您可以互换使用 "2-wise""pairwise" 属性值。"n-wise" 参数组合概括成对组合与穷举组合相比,通常会导致测试次数更少,因此测试执行速度更快。虽然框架可保证至少为每个值的 n 元组创建一次测试,但您不应依赖套件大小、顺序或特定的测试套件元素集。

您可以在类设置、方法设置和测试级别组合参数。例如,使用两个方法特性 TestMethodSetup,ParameterCombination="sequential" 来指定在 properties 代码块中定义的、具有 MethodSetupParameter 特性的方法设置级别参数的顺序组合。

有关如何组合测试参数的示例,请参阅创建基本参数化测试创建高级参数化测试

在测试中使用外部参数

创建参数化测试时,可以通过将输入注入基于类的测试中来重新定义参数。要提供在测试文件之外定义的数据,请创建一个 Parameter 实例,并在创建测试套件时使用 ExternalParameter 名称-值参数。有关详细信息,请参阅在参数化测试中使用外部参数

另请参阅

命名空间

相关主题