Matlab 单元测试在通过和失败之间交替(通过 "odd" 运行,失败 "even")

Matlab unit tests alternate between pass and fail (pass "odd" runs, fail on "even")

我对执行一些非常小的图像处理(将几个小图像组合成一个大图像)的代码进行了一些单元测试。当我 运行 进行测试时,我注意到四分之三的测试在从目录读取图像的那一行失败了(失败并出现索引越界错误)。

但是,如果我再运行它一次,它们都通过了。当我也在编写代码时,我注意到每当我在代码中设置断点时,我都必须 运行 单元测试两次,因为(在第一次之后)它会 运行通过测试,没有遇到任何断点。

我的仓库是这样组织的:

src/
    /* source code .m files are in here */

unit_tests/
    images/
        squares/
            - img1.png
            - img2.png
            ...
            - imgn.png
    - unit_tests.m

我的设置中有一行(在 unit_tests.m 内)为所有代码生成和添加路径:

function tests = unit_tests()
    addpath(genpath('..'));    
    tests = functiontests(localfunctions);
end

单元测试都采用这种格式:

function testCompositeImage_2x3(testCase)
    squares = dir('images/squares/*.png');
    num_images = length(squares);
    img = imread([squares(1).folder filesep squares(1).name]); % all same size squares
    rows = 2;
    cols = 3;
    buffer = 2;

    for idx = 1:num_images - (rows*cols)
        imarray = cell(1,(rows*cols));
        n = 1;
        for ii = idx:idx +(rows*cols) -1
            imarray{n} = imread([squares(ii).folder filesep squares(ii).name]);
            n = n + 1;
        end

        newimg = createCompositeImage(rows,cols,imarray, buffer);

        expCols = cols*size(img,1) + (cols+1)*2*buffer;
        expRows = rows*size(img,2) + (rows+1)*2*buffer;
        assert(checksize(newimg, expRows, expCols, 3) == true);
    end
end

("checksize" 只是我写的一个帮手 returns 布尔值 b/c 断言不比较矩阵)

当我启动一个新的 matlab 会话和 运行 单元测试(使用编辑器选项卡中的 "Run Tests" 按钮)时,它们通过了以下输出:

>> runtests('unit_tests\unit_tests.m')
Running unit_tests
.......
Done unit_tests
__________


ans = 

  1×7 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   7 Passed, 0 Failed, 0 Incomplete.
   0.49467 seconds testing time.

运行第二次(再次按下按钮):

>> runtests('unit_tests')
Running unit_tests
..
================================================================================
Error occurred in unit_tests/testCompositeImage_2x2 and it did not run to completion.

    ---------
    Error ID:
    ---------
    'MATLAB:badsubscript'

    --------------
    Error Details:
    --------------
    Index exceeds array bounds.

    Error in unit_tests>testCompositeImage_2x2 (line 47)
        img = imread([squares(1).folder filesep squares(1).name]); % all same size
================================================================================
/*similar error info for the other two failing tests...*/
...
Done unit_tests
__________

Failure Summary:

     Name                               Failed  Incomplete  Reason(s)
    ==================================================================
     unit_tests/testCompositeImage_2x2    X         X       Errored.
    ------------------------------------------------------------------
     unit_tests/testCompositeImage_2x3    X         X       Errored.
    ------------------------------------------------------------------
     unit_tests/testCompositeImage_3x2    X         X       Errored.


ans = 

  1×7 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   4 Passed, 3 Failed (rerun), 3 Incomplete.
   0.0072287 seconds testing time.

它基本上在第一行就失败了,因为它没有从文件夹中读取任何内容,这让我怀疑即使其他 4 个测试应该通过了,但实际上它们根本就没有 运行ning .然而,如果我再次 运行 测试,它们都会通过。 运行第4次,他们又失败了。

起初,我认为单元测试可能执行得太快了(只在偶数 运行s 上执行?)并且 运行 在 [=] 之前执行单元测试42=] 设置中的函数已经完成,所以我添加了一个暂停语句并重新 运行 测试,但是我只有这次遇到了同样的问题它会等待必要的秒数然后继续并失败.如果我再次 运行,没问题 - 我的所有测试都通过了。

我完全不知道为什么会这样;我在 Win10 机器上使用 vanilla matlab (R2018a) 运行ning 并且没有任何花哨的事情发生。我觉得您应该可以 运行 多次进行单元测试,并期望获得相同的结果!有什么我只是以某种方式忽略了吗?或者这是一些奇怪的功能?

添加我的修复程序以防其他人遇到同样的问题。

正如 Cris 所指出的,关于

这行的一些事情
addpath(genpath('..'));

导致 GUI 进入一个奇怪的状态,按下 "Run Tests" 按钮在调用 runtests('unit_tests\unit_tests.m')runtests('unit_tests') 之间交替,这反过来导致测试交替通过和失败。它似乎不是路径变量本身的问题(因为它总是包含 - 至少 - 必要的目录),而是 matlab 本身固有的问题。我最接近问题根源的是调用 genpath 函数中的(已编译)dir 函数。

"correct" 解决方案是从 unit_tests 函数中完全删除该行并将其添加到 setupOnce 函数中:

function tests = unit_tests()
    tests = functiontests(localfunctions);
end

function setupOnce(testCase)
    addpath(genpath('..'));
end

不需要 setupOnce 函数的 hack(不推荐)如下:

function tests = unit_tests()
    pth = fullfile(fileparts(fileparts(mfilename('fullpath'))),'src');
    paths = regexp(genpath(pth), ';', 'split');
    for idx = 1:length(paths) - 1 % last element is empty
        addpath(paths{idx});
    end
end

我需要重新启动 matlab 才能使更改生效。这适用于我在 Win10 上使用 r2018a 运行 的设置。