Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/matlab/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 为单元测试公开M文件子功能的最简单方法是什么?_Unit Testing_Matlab - Fatal编程技术网

Unit testing 为单元测试公开M文件子功能的最简单方法是什么?

Unit testing 为单元测试公开M文件子功能的最简单方法是什么?,unit-testing,matlab,Unit Testing,Matlab,我最近一直在修修补补将连续测试完全集成到我的Matlab开发周期中,遇到了一个我不知道如何解决的问题。几乎所有用户都知道,Matlab很友好地将M文件中的子函数隐藏在该M文件之外的任何函数的视图中。玩具示例如下所示: function [things] = myfunc(data) [stuff] = mysubfunc(data) things = mean(stuff); end 我想在subfunc本身上执行单元测试。这是不可能的,因为我不能从任何外部函数调用它 我目前正在使用S

我最近一直在修修补补将连续测试完全集成到我的Matlab开发周期中,遇到了一个我不知道如何解决的问题。几乎所有用户都知道,Matlab很友好地将M文件中的子函数隐藏在该M文件之外的任何函数的视图中。玩具示例如下所示:

function [things] = myfunc(data)
  [stuff] = mysubfunc(data)
  things = mean(stuff);
end
我想在subfunc本身上执行单元测试。这是不可能的,因为我不能从任何外部函数调用它


我目前正在使用Steve Eddins的Matlab xUnit,无法回避这个问题。这种简单的解决方案——将子UNC拆分为它自己的M文件——在实践中是不可接受的,因为我将有许多小函数要测试,并且不希望每个函数都有一个单独的M文件来污染我的文件系统。如何编写和执行简单的单元测试,而不为每个要测试的函数创建新文件?

我有一种非常简单的方法。不完美,但至少是可能的

function [things] = myfunc(data)

global TESTING

if TESTING == 1
    unittests()
else
    [stuff] = mysubfunc(data);
    things = mean(stuff);
end

end

function unittests()

%%Test one
tdata = 1;
assert(mysubfunc(tdata) == 3)

end

function [stuff] = mysubfunc(data)

stuff = data + 1;

end
然后在提示下,这将完成以下操作:

>> global TESTING; TESTING = 1; myfunc(1)
??? Error using ==> myfunc>unittests at 19
Assertion failed.

Error in ==> myfunc at 6
    unittests()

>> TESTING = 0; myfunc(1)

ans =

     2

>> 

通常,您需要做的是从主函数内部访问子函数,并将它们传递到函数外部,以便对它们进行单元测试。一种方法是修改主函数,这样,给定一组特定的输入参数(即无输入、参数的某些标志值等),它将返回所需的函数句柄

例如,可以在函数开头添加几行代码,以便在未指定输入时返回所有子函数句柄:

function things = myfunc(data)

  if nargin == 0                            % If data is not specified...
    things = {@mysubfunc @myothersubfunc};  % Return a cell array of
                                            %   function handles
    return                                  % Return from the function
  end

  % The normal processing for myfunc...
  stuff = mysubfunc(data);
  things = mean(stuff);

end

function mysubfunc
  % One subfunction
end

function myothersubfunc
  % Another subfunction
end
或者,如果您更喜欢指定输入标志(以避免与意外调用Jonas在其注释中提到的没有输入的函数相关的任何混淆),则可以在输入参数
data
是特定字符串时返回子函数句柄。例如,您可以将上述代码中的输入检查逻辑更改为:

if ischar(data) && strcmp(data, '-getSubHandles')

我使用了一种方法,它反映了GUIDE生成其输入方法的方式。就算它对GUI有偏见

富美

这允许您执行以下操作

%通过10和1调用foo中的bar

foo('bar',10,1)

您使用过新样式的类吗?您可以将该函数转换为实用程序类上的静态方法。然后,您可以将子函数转换为其他静态方法,或者将子函数转换为类的本地函数,并为类提供一个将句柄返回给它们的静态方法

classdef fooUtil
    methods (Static)
        function [things] = myfunc(data)
            [stuff] = mysubfunc(data);
            things = mean(stuff);
        end

        function out = getLocalFunctionHandlesForTesting()
            onlyAllowThisInsideUnitTest();
            out.mysubfunc = @mysubfunc;
            out.sub2 = @sub2;
        end
    end
end

% Functions local to the class
function out = mysubfunc(x)
    out = x .* 2; % example dummy logic
end
function sub2()
    % ...
end

function onlyAllowThisInsideUnitTest()
%ONLYALLOWTHISINSIDEUNITTEST Make sure prod code does not depend on this encapsulation-breaking feature
    isUnitTestRunning = true; % This should actually be some call to xUnit to find out if a test is active
    assert(isUnitTestRunning, 'private function handles can only be grabbed for unit testing');
end
如果您使用classdef样式语法,那么所有这些函数和任何其他方法都可以放在一个fooUtil.m文件中;没有文件系统混乱。或者,您可以在类内部编写测试代码,而不是公开私有内容

我认为单元测试纯粹主义者会说,你根本不应该这样做,因为你应该针对对象的公共接口进行测试,如果你需要测试子部分,那么应该将它们分解为在其公共接口中呈现它们的其他部分。本文主张将它们全部公开为静态方法,并直接针对它们进行测试,而忘记了使用函数句柄公开私有函数

classdef fooUtil
    methods (Static)
        function [things] = myfunc(data)
            [stuff] = fooUtil.mysubfunc(data);
            things = mean(stuff);
        end
        function out = mysubfunc(x)
            out = x .* 2; % example dummy logic
        end
        function sub2()
            % ...
        end
    end
end            

也就是说,我根本没有公开子函数:)我将使用一个特殊的输入参数来实现这一点,而不是使用一个全局变量。把这个答案和+1结合起来,我认为这是最好的方法(一个会触发测试模式的输入标志[像一个隐藏/未记录的特性])…+1-尽管我会使用
returnSubfunctionHandles
输入参数创建函数句柄。调用一个本来需要零输入参数输入的函数是一个偶尔会发生的错误,并且可能需要一段时间才能找到这些奇怪句柄的来源。我发现这个答案非常有用,并发布了一个相关问题,关于如何自动生成要返回的子函数列表。在2013b以后,有一种使用
localfunctions
实现这一点的简单方法:我认为单元测试应该在待测试代码所在的位置编写。在这种情况下,
myfunc-runTests
myfunc-getSubHandles
更符合逻辑。
classdef fooUtil
    methods (Static)
        function [things] = myfunc(data)
            [stuff] = fooUtil.mysubfunc(data);
            things = mean(stuff);
        end
        function out = mysubfunc(x)
            out = x .* 2; % example dummy logic
        end
        function sub2()
            % ...
        end
    end
end