Main Content

创建高级参数化测试

此示例演示如何创建在 TestClassSetupTestMethodSetupTest methods 块中被参数化的测试。示例测试类测试随机数生成。

创建 TestRand 测试类

在当前文件夹的一个文件中创建 TestRand 类来测试随机数生成的各个方面。定义用于参数化测试的属性。

classdef TestRand < matlab.unittest.TestCase
    properties (ClassSetupParameter)
        generator = {'twister','combRecursive','multFibonacci'};
    end
    
    properties (MethodSetupParameter)
        seed = {0,123,4294967295};
    end
    
    properties (TestParameter)
        dim1 = struct('small',1,'medium',2,'large',3);
        dim2 = struct('small',2,'medium',3,'large',4);
        dim3 = struct('small',3,'medium',4,'large',5);
        type = {'single','double'};
    end
end

TestRand 类中,每个 properties 块对应于一个特定级别的参数化。类设置级别的参数化定义随机数生成器的类型。方法设置级别参数化定义随机数生成器的种子,测试级别参数化定义随机值的数据类型和大小。

定义测试类和测试方法设置方法

定义测试类和测试方法级别的设置方法。这些方法注册初始随机数生成器状态。该框架运行测试后,这些方法将恢复原始状态。classSetup 方法定义随机数生成器的类型,methodSetup 方法为生成器提供种子。

classdef TestRand < matlab.unittest.TestCase
    properties (ClassSetupParameter)
        generator = {'twister','combRecursive','multFibonacci'};
    end
    
    properties (MethodSetupParameter)
        seed = {0,123,4294967295};
    end
    
    properties (TestParameter)
        dim1 = struct('small',1,'medium',2,'large',3);
        dim2 = struct('small',2,'medium',3,'large',4);
        dim3 = struct('small',3,'medium',4,'large',5);
        type = {'single','double'};
    end
    
    methods (TestClassSetup)
        function classSetup(testCase,generator)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(0,generator)
        end
    end
    
    methods (TestMethodSetup)
        function methodSetup(testCase,seed)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(seed)
        end
    end
end

用顺序参数组合定义测试方法

在具有 TestParameterCombination = 'sequential' 特性的 methods 代码块中定义 testSize 方法。

classdef TestRand < matlab.unittest.TestCase
    properties (ClassSetupParameter)
        generator = {'twister','combRecursive','multFibonacci'};
    end
    
    properties (MethodSetupParameter)
        seed = {0,123,4294967295};
    end
    
    properties (TestParameter)
        dim1 = struct('small',1,'medium',2,'large',3);
        dim2 = struct('small',2,'medium',3,'large',4);
        dim3 = struct('small',3,'medium',4,'large',5);
        type = {'single','double'};
    end
    
    methods (TestClassSetup)
        function classSetup(testCase,generator)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(0,generator)
        end
    end
    
    methods (TestMethodSetup)
        function methodSetup(testCase,seed)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(seed)
        end
    end
    
    methods (Test, ParameterCombination = 'sequential')
        function testSize(testCase,dim1,dim2,dim3)
            testCase.verifySize(rand(dim1,dim2,dim3),[dim1 dim2 dim3])
        end
    end
end

该方法可用于测试 dim1dim2dim3 中的每个对应的参数值的输出的大小。对于给定的 TestClassSetupTestMethodSetup 参数化值,该框架调用 testSize 方法三次 - 分别针对 'small''medium''large' 值调用一次。例如,要用所有 'medium' 值进行测试,框架使用 testCase.verifySize(rand(2,3,4),[2 3 4])

使用成对参数组合定义测试方法

在具有 TestParameterCombination = 'pairwise' 特性的 methods 代码块中定义 testRepeatable 方法。

classdef TestRand < matlab.unittest.TestCase
    properties (ClassSetupParameter)
        generator = {'twister','combRecursive','multFibonacci'};
    end
    
    properties (MethodSetupParameter)
        seed = {0,123,4294967295};
    end
    
    properties (TestParameter)
        dim1 = struct('small',1,'medium',2,'large',3);
        dim2 = struct('small',2,'medium',3,'large',4);
        dim3 = struct('small',3,'medium',4,'large',5);
        type = {'single','double'};
    end
    
    methods (TestClassSetup)
        function classSetup(testCase,generator)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(0,generator)
        end
    end
    
    methods (TestMethodSetup)
        function methodSetup(testCase,seed)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(seed)
        end
    end
    
    methods (Test, ParameterCombination = 'sequential')
        function testSize(testCase,dim1,dim2,dim3)
            testCase.verifySize(rand(dim1,dim2,dim3),[dim1 dim2 dim3])
        end
    end
    
    methods (Test, ParameterCombination = 'pairwise')
        function testRepeatable(testCase,dim1,dim2,dim3)
            state = rng;
            firstRun = rand(dim1,dim2,dim3);
            rng(state)
            secondRun = rand(dim1,dim2,dim3);
            testCase.verifyEqual(firstRun,secondRun)
        end
    end
end

该方法验证随机数生成器结果是否可重复。对于给定的 TestClassSetupTestMethodSetup 参数化值,该框架调用 testRepeatable 方法 10 次,以确保由 dim1dim2dim3 指定的每对参数值都经过测试。如果参数组合以穷举方式提供,该框架将调用该方法 3³ = 27 次。

使用穷举参数组合定义测试方法

在具有 Test 特性的 methods 代码块中定义 testClass 方法。由于未指定 ParameterCombination 特性,默认情况下参数组合以穷举方式提供。

classdef TestRand < matlab.unittest.TestCase
    properties (ClassSetupParameter)
        generator = {'twister','combRecursive','multFibonacci'};
    end
    
    properties (MethodSetupParameter)
        seed = {0,123,4294967295};
    end
    
    properties (TestParameter)
        dim1 = struct('small',1,'medium',2,'large',3);
        dim2 = struct('small',2,'medium',3,'large',4);
        dim3 = struct('small',3,'medium',4,'large',5);
        type = {'single','double'};
    end
    
    methods (TestClassSetup)
        function classSetup(testCase,generator)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(0,generator)
        end
    end
    
    methods (TestMethodSetup)
        function methodSetup(testCase,seed)
            orig = rng;
            testCase.addTeardown(@rng,orig)
            rng(seed)
        end
    end
    
    methods (Test, ParameterCombination = 'sequential')
        function testSize(testCase,dim1,dim2,dim3)
            testCase.verifySize(rand(dim1,dim2,dim3),[dim1 dim2 dim3])
        end
    end
    
    methods (Test, ParameterCombination = 'pairwise')
        function testRepeatable(testCase,dim1,dim2,dim3)
            state = rng;
            firstRun = rand(dim1,dim2,dim3);
            rng(state)
            secondRun = rand(dim1,dim2,dim3);
            testCase.verifyEqual(firstRun,secondRun)
        end
    end
    
    methods (Test)
        function testClass(testCase,dim1,dim2,type)
            testCase.verifyClass(rand(dim1,dim2,type),type)
        end
    end
end

该方法验证从 rand 输出的类是否与预期的类相同。对于给定的 TestClassSetupTestMethodSetup 参数化值,该框架调用 testClass 方法 3×3×2 = 18 次,以确保 dim1dim2type 参数值的每个组合都经过测试。

基于测试类创建套件

在命令提示符下,基于 TestRand 类创建一个套件。

suite = matlab.unittest.TestSuite.fromClass(?TestRand)
suite = 

  1×279 Test array with properties:

    Name
    ProcedureName
    TestClass
    BaseFolder
    Parameterization
    SharedTestFixtures
    Tags

Tests Include:
   17 Unique Parameterizations, 0 Shared Test Fixture Classes, 0 Tags.

对于给定的 TestClassSetupTestMethodSetup 参数化值,该框架创建 3+10+18 = 31 个测试元素。对每个 TestMethodSetup 参数化值都调用这 31 个元素,会为每个 TestClassSetup 参数化值生成 3×31 = 93 个测试元素。由于共有三个 TestClassSetup 参数化值;因此,该套件共有 3×93 = 279 个测试元素。

查询第一个测试元素的名称。

suite(1).Name
ans =

    'TestRand[generator=twister]/[seed=0]testClass(dim1=small,dim2=small,type=single)'

第一个元素的名称由以下各部分组成:

  • 测试类 - TestRand

  • 类设置属性和参数名称 - [generator=twister]

  • 方法设置属性和参数名称 - [seed=0]

  • 测试方法名称 - testClass

  • 测试方法属性和参数名称 - (dim1=small,dim2=small,type=single)

运行使用选择器创建的套件

在命令提示符下,创建一个选择器以选择用于测试 'twister' 生成器的 'single' 精度的测试元素。创建一个套件,忽略那些属性中包含 'large' 参数名称的测试元素。

import matlab.unittest.selectors.HasParameter
s = HasParameter('Property','generator','Name','twister') & ...
    HasParameter('Property','type','Name','single') & ...
    ~HasParameter('Name','large');

suite2 = matlab.unittest.TestSuite.fromClass(?TestRand,s)
suite2 = 

  1×12 Test array with properties:

    Name
    ProcedureName
    TestClass
    BaseFolder
    Parameterization
    SharedTestFixtures
    Tags

Tests Include:
   9 Unique Parameterizations, 0 Shared Test Fixture Classes, 0 Tags.

如果您首先基于 TestRand 类生成完整套件,则可以使用 selectIf 方法构造相同的经过过滤的套件。

suite = matlab.unittest.TestSuite.fromClass(?TestRand);
suite2 = selectIf(suite,s);

运行过滤后的测试套件。

suite2.run;
Running TestRand
.......... ..
Done TestRand
__________

使用选择器从方法运行套件

创建一个选择器,忽略那些属性中包含 'large''medium' 参数名称的测试元素。将结果限制为来自 testRepeatable 方法的测试元素。

import matlab.unittest.selectors.HasParameter
s = ~(HasParameter('Name','large') | HasParameter('Name','medium'));

suite3 = matlab.unittest.TestSuite.fromMethod(?TestRand,'testRepeatable',s);
{suite3.Name}'
ans =

  9×1 cell array

    {'TestRand[generator=twister]/[seed=0]testRepeatable(dim1=small,dim2=small,dim3=small)'               }
    {'TestRand[generator=twister]/[seed=123]testRepeatable(dim1=small,dim2=small,dim3=small)'             }
    {'TestRand[generator=twister]/[seed=4294967295]testRepeatable(dim1=small,dim2=small,dim3=small)'      }
    {'TestRand[generator=combRecursive]/[seed=0]testRepeatable(dim1=small,dim2=small,dim3=small)'         }
    {'TestRand[generator=combRecursive]/[seed=123]testRepeatable(dim1=small,dim2=small,dim3=small)'       }
    {'TestRand[generator=combRecursive]/[seed=4294967295]testRepeatable(dim1=small,dim2=small,dim3=small)'}
    {'TestRand[generator=multFibonacci]/[seed=0]testRepeatable(dim1=small,dim2=small,dim3=small)'         }
    {'TestRand[generator=multFibonacci]/[seed=123]testRepeatable(dim1=small,dim2=small,dim3=small)'       }
    {'TestRand[generator=multFibonacci]/[seed=4294967295]testRepeatable(dim1=small,dim2=small,dim3=small)'}

运行测试套件。

suite3.run;
Running TestRand
.........
Done TestRand
__________

运行所有双精度测试

在命令提示符下,运行 TestRand 类中使用 'double' 参数名称的所有测试元素。

runtests('TestRand','ParameterName','double');
Running TestRand
.......... .......... .......... .......... ..........
.......... .......... .......... .
Done TestRand
__________

另请参阅

| |

相关主题