本页对应的英文页面已更新,但尚未翻译。 若要查看最新内容,请点击此处访问英文页面。

创建高级参数化测试

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

测试概述

TestRand 测试类在三个不同级别被参数化。

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

在每个测试级别,您可以使用 ParameterCombination 方法属性指定测试参数化。

ParameterCombination 属性方法调用
'exhaustive'(默认值)对所有的参数组合调用方法。如何您不指定 ParameterCombination 属性,则测试框架使用此默认组合。
'sequential'通过每个参数中的相应值调用方法。每个参数必须包含相同数量的值。
'pairwise'至少对每对参数值调用一次方法。虽然测试框架可保证至少为每对值创建一次测试,但您不应依赖该大小、顺序或特定组的测试套件元素。

例如,使用组合的方法属性 TestMethodSetup, ParameterCombination='sequential' 指定 MethodSetupParameter 属性块中定义的方法设置级别参数的顺序组合。

对于此示例,类设置级别参数化定义随机数生成器的类型。方法设置级别参数化定义随机数生成器的种子,测试级别参数化定义随机数输出的数据类型和大小。

创建 TestRand 测试类

在您的工作文件夹下的文件中,创建一个从 matlab.unittest.TestCase 继承的类。该类测试随机数生成的各个方面。

classdef TestRand < matlab.unittest.TestCase

定义属性块

定义用于参数化测试的属性。每个 properties 块对应于特定级别的参数化。

    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

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

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

    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

定义顺序参数化的测试方法

使用 TestParameterCombination='sequential' 属性定义 methods 块。测试框架对每个对应的属性值调用这些方法一次。

    methods (Test, ParameterCombination='sequential')
        function testSize(testCase,dim1,dim2,dim3)
            testCase.verifySize(rand(dim1,dim2,dim3),[dim1 dim2 dim3])
        end 
    end

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

定义成对的参数化测试方法

使用 TestParameterCombination='pairwise' 属性定义 methods 块。测试框架对每对属性值至少调用这些方法一次。

    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

测试方法验证随机数生成器结果是否可重复。对于给定的 TestClassSetupTestMethodSetup 参数化,该框架调用 testRepeatble 方法 10 次以确保测试每对 dim1dim2dim3。但是,如果参数组合属性是 Exhaustive,该框架调用该方法 3^3=27 次。

定义 Exhaustive 参数化测试方法

使用 Test 属性或未定义的参数组合定义 methods 块。该参数组合默认为 exhaustive。测试框架对每个属性值组合调用这些方法一次。

    methods (Test)
        function testClass(testCase,dim1,dim2,type)
            testCase.verifyClass(rand(dim1,dim2,type), type)
        end
    end

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

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
    
    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

从所有测试创建套件

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

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.

该测试套件包含 279 个测试元素。对于给定的 TestClassSetupTestMethodSetup 参数化,该框架创建 3+10+18=31 个测试元素。这 31 个元素被调用三次 - 每个 TestMethodSetup 参数化一次,从而为每个 TestClassSetup 参数化生成 3*31=93 个测试元素。存在三个 TestClassSetup 参数化,生成总计 3*93=279 个测试元素。

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

suite(1).Name
ans =

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

每个元素的名称是根据以下项的组合构造的:

  • 测试类:TestRand

  • 类设置属性和属性名:[generator=twister]

  • 方法设置属性和属性名:[seed=value1]

  • 测试方法名称:testClass

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

seed 属性的名称并不是特别有意义 (value1)。测试框架提供此名称是因为 seed 属性值是数字。要使名称更有意义,请使用说明性更强的字段名称将 seed 属性定义为结构体。

使用选择器从类运行套件

在命令提示符下,创建一个选择器以选择用于测试 '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.

如果您首先生成完整套件,请使用 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=value1]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=twister]/[seed=value2]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=twister]/[seed=value3]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=combRecursive]/[seed=value1]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=combRecursive]/[seed=value2]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=combRecursive]/[seed=value3]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=multFibonacci]/[seed=value1]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=multFibonacci]/[seed=value2]testRepeatable(dim1=small,dim2=small,dim3=small)'
    'TestRand[generator=multFibonacci]/[seed=value3]testRepeatable(dim1=small,dim2=small,dim3=small)'

运行测试套件。

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

运行所有双精度测试

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

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

另请参阅

| |

相关主题