如何使用MATLAB写测试(3)combinatorial explosion? 参数化!

发布于:2024-06-05 ⋅ 阅读:(130) ⋅ 点赞:(0)

如何使用MATLAB写测试(3)combinatorial explosion? 参数化!

原文:如何使用MATLAB写测试(3)combinatorial explosion? 参数化! - 知乎 (zhihu.com)

最近我们的俄罗斯实习生有些头大,他为foo程序写的正向测试只有single一种情况。

function out = foo(in)
    validateattributes(in,{'numeric'},{'nonempty'}); 
    % Returns zero
    out = zeros(size(in),'like',in);
end

他的老板看到他的测试后问他,你知道MATLAB中数据类型有几种吗?俄罗斯实习生虽然没读过孔乙己的故事,但至少也知道有double, single, uint8几种常见的类型。于是他尝试更新了下他的测试。

classdef myTest < matlab.unittest.TestCase
    
    methods (Test)
        function testSingle(test) %function唯一的参数test是你的测试对象
            % Verifies single input case
            in        = single(10);             %输入
            expOut    = zeros(1,'single');      %期待的输出
            actualOut = foo(in);                %调用待测程序
            test.verifyEqual(actualOut,expOut); %比较实际输出与期待输出
        end

        function testDouble(test)
            % Verifies double input case
            in        = double(10);             %输入
            expOut    = zeros(1,'double');      %期待的输出
            actualOut = foo(in);                %调用待测程序
            test.verifyEqual(actualOut,expOut); %比较实际输出与期待输出
        end

        % Negative test case
        function testEmptyError(test)
            % Verifies error on empty input
            in         = [];
            expErrorId = 'MATLAB:expectedNonempty';
            %传入function handle, 给出期待的error id
            test.verifyError(@()foo(in),expErrorId);            
        end
    end
end

作为一个合格的实习生,他一下就发现了接下来会发生的事情。对于每一个数据类型,他都要写一个完全一样的测试。公司招他来可不是做ctrl+c,ctrl+v的。而且要是以后有更多的测试,每个都需要对不同数据类型做相同的test case, 那要多少啊?

还好我们的数学教育不差

他心想。手上迅速算着帐。

假设MATLAB有N种数据类型,而我为每个数据类型都需要写M个测试,假设M个测试对每个数据类型都是相同的,那我就会有
𝐶𝑁1∗𝑀=𝑁∗𝑀
个测试,这还是以我的程序需要一个输入为前提。
这是如果我的程序需要两个输入,比如foo(x,y)
那我要写的测试数量就变成了
𝐶𝑁1∗𝐶𝑁1∗𝑀=𝑁2∗𝑀
...
如果程序需要k个输入,那要写的测试数量就变成了
𝑁𝑘∗𝑀

想到这里他背后直冒冷汗,难道自己的一生就耗在写测试上了?

MATLAB这么高大上的语言一定不会允许这件事发生的。

他边这么想着边翻阅着文档,突然发现了Parametrized Tests这篇教程。他按部就班地更新了自己的测试

classdef myTest < matlab.unittest.TestCase    
    properties(TestParameter) %定义测试参数
        type = {'double','single','uint8'} 
    end
    methods (Test)
        function testInput(test, type) %传入测试参数
            % Verifies single input case
            in        = cast(10,type);           %输入
            expOut    = zeros(1,type);           %期待的输出
            actualOut = foo(in);                 %调用待测程序
            test.verifyEqual(actualOut,expOut);  %比较实际输出与期待输出
        end
    end
end

跑test

>> result = runtests('myTest')
Running myTest
...
Done myTest
__________


result = 

  1x3 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   3 Passed, 0 Failed, 0 Incomplete.
   0.12308 seconds testing time.

注意到了吗,虽然只写了1个test case, 但是结果result中却有3个TestResult。展开看看究竟是什么?

>> result.Name

ans =

myTest/testInput(supportedClass=double)


ans =

myTest/testInput(supportedClass=single)


ans =

myTest/testInput(supportedClass=uint8)

哦!

掌握了基本参数化测试的俄罗斯实习生决定试试更高级的技巧,他为自己的源程序加入了一个新的输入

function out = foo(in1 , in2)
    assert(isequal(size(in1),size(in2)),'input 1 and input 2 must be of same size'); % ugly but works
    validateattributes(in1,{'numeric'},{'nonempty'}); 
    validateattributes(in2,{'numeric'},{'nonempty'}); 

    % Cast everything into input 1's class
    in2 = cast(in2,class(in1));
    % Returns zero
    out = zeros(size(in1),'like',in1) * zeros(size(in2),'like',in2) ;
end

并更新了测试

classdef myTest < matlab.unittest.TestCase    
    properties(TestParameter) %定义测试参数
        type1 = {'double','single','uint8'} 
        type2 = {'double','single','uint8'} 
    end
    methods (Test)
        function testInput(test, type1, type2) %传入测试参数
            % Verifies single input case
            in1        = cast(10,type1); %输入
            in2        = cast(10,type2); %输入
            expOut     = zeros(1,type1); %期待的输出
            actualOut  = foo(in1,in2);   %调用待测程序
            test.verifyEqual(actualOut,expOut);  %比较实际输出与期待输出
        end
    end
end

跑tests

>> result = runtests('myTest')
Running myTest
.........
Done myTest
__________


result = 

  1x9 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   9 Passed, 0 Failed, 0 Incomplete.
   0.10923 seconds testing time.

有9个测试了,心算下……

3个数据类型,2个输入,1个测试方法
𝑁=3,𝑘=2,𝑀=1,𝑁𝑘∗𝑀=32∗1=9

对哦!

这下combinatorial explosion的问题就给计算机去解决吧!我1000刀买来配Oculus Rift的机器跑这点test还是可以的……

有兴趣的玩家(读者 可以阅读如何利用ParameterCombination来减少测试的数量

-参考资料

Create Basic Parameterized Test

Create Advanced Parameterized Test


网站公告

今日签到

点亮在社区的每一天
去签到