Main Content

编写使用 App 测试和模拟框架的测试

此示例说明如何编写使用 App 测试框架和模拟框架的测试。该 App 包含一个文件选择对话框和一个指示所选文件的标签。要以编程方式测试该 App,请使用一个 mock 对象来定义文件选择器的行为。

创建 App

在您当前文件夹中创建 launchApp。该 App 允许用户选择一个输入文件并在 App 中显示文件的名称。文件选择对话框是一个等待用户输入的阻断型模态对话框。

function app = launchApp
f = uifigure;
button = uibutton(f,"Text","Input file");
button.ButtonPushedFcn = @(src,event) pickFile;
label = uilabel(f,"Text","No file selected");
label.Position(1) = button.Position(1) + button.Position(3) + 25;
label.Position(3) = 200;

% Add components to app
app.UIFigure = f;
app.Button = button;
app.Label = label;

    function file = pickFile
        [file,~,status] = uigetfile("*.*");
        if status
            label.Text = file;
        end
    end
end

要在测试之前浏览 App 的属性,请在命令提示符下调用 launchApp 函数。此步骤对于测试不是必需的,但是了解 App 测试使用的属性会很有帮助。例如,使用 app.Button 访问 App 对象内的输入文件按钮。

app = launchApp;
app.Button
ans = 

  Button (Input file) with properties:

               Text: 'Input file'
               Icon: ''
    ButtonPushedFcn: @(src,event)pickFile
           Position: [100 100 100 22]

  Show all properties

App window displaying the Input file button and the text "No file selected"

在手动干预下测试 App

在不使用仿件的情况下创建 LaunchAppTest 测试类。该测试要求文件 input.txt 存在于您的当前文件夹中。如果它不存在,请创建它。该测试以编程方式按下 Input file 按钮,并验证标签是否匹配 'input.txt'。您必须手动选择文件。

classdef LaunchAppTest < matlab.uitest.TestCase
    properties
        Filename = 'input.txt'
    end
    methods(TestClassSetup)
        function checkFile(testCase)
            import matlab.unittest.constraints.IsFile
            testCase.assertThat(testCase.Filename,IsFile)
        end
    end
    methods (Test)
        function testInput(testCase)
            app = launchApp;
            testCase.addTeardown(@close,app.UIFigure)

            testCase.press(app.Button)

            testCase.verifyEqual(app.Label.Text,testCase.Filename)
        end
    end
end

运行测试。当文件选择对话框出现时,选择 input.txt 以允许 MATLAB 继续测试。选择任何其他文件会导致测试失败。

runtests("LaunchAppTest");
Running LaunchAppTest
.
Done LaunchAppTest
__________

创建全自动测试

要测试 App 而无需手动干预,请使用模拟框架。将 App 修改为接受文件选择服务,而不是在 App 中实现该服务(依赖项注入)。

使用 Abstract 方法创建一个 FileChooser 服务,用于实现文件选择功能。

classdef FileChooser
    % Interface to choose a file
    methods (Abstract)
        [file,folder,status] = chooseFile(chooser,varargin)
    end
end

创建一个默认 FileChooser 服务,它使用 uigetfile 函数进行文件选择。

classdef DefaultFileChooser < FileChooser
    methods
        function [file,folder,status] = chooseFile(~,varargin)
            [file,folder,status] = uigetfile(varargin{:});
        end
    end
end

将 App 更改为接受可选的 FileChooser 对象。当在没有输入的情况下调用时,该 App 使用 DefaultFileChooser 的实例。

function app = launchApp(fileChooser)
arguments
    fileChooser (1,1) FileChooser = DefaultFileChooser
end

f = uifigure;
button = uibutton(f,"Text","Input file");
button.ButtonPushedFcn = @(src,event) pickFile(fileChooser);
label = uilabel(f,"Text","No file selected");
label.Position(1) = button.Position(1) + button.Position(3) + 25;
label.Position(3) = 200;

% Add components to app
app.UIFigure = f;
app.Button = button;
app.Label = label;

    function file = pickFile(fileChooser)
        [file,~,status] = fileChooser.chooseFile("*.*");
        if status
            label.Text = file;
        end
    end
end

LaunchAppTest 进行以下修改:

  • 将该类更改为从 matlab.uitest.TestCasematlab.mock.TestCase 中继承。

  • 删除 properties 代码块和 TestClassSetup methods 代码块。由于 mock 定义 chooseFile 方法调用的相应输出,因此测试不依赖于外部文件的存在。

  • 更改 testInput 方法以执行以下操作:

    • FileChooser 创建一个 mock 对象。

    • 定义 mock 行为,以便在使用输入 "*.*" 调用 chooseFile 方法时,输出为文件名 ('input.txt')、当前文件夹和选定的过滤器索引 1。这些输出类似于 uigetfile 函数的输出。

    • 使用 mockChooser 对象启动 App。

    • 按下按钮并验证所选文件的名称。这些操作与原始测试中的步骤相同,但 mock 会分配输出值,因此您无需与 App 交互即可继续测试。

  • 要测试取消按钮,请添加名为 testCancelTest 方法来执行以下操作:

    • FileChooser 创建一个 mock 对象。

    • 定义 mock 行为,以便在使用输入 "*.*" 调用 chooseFile 方法时,输出为文件名 ('input.txt')、当前文件夹和选定的过滤器索引 0。这些输出类似于在用户选择一个文件然后选择取消时 uigetfile 函数的输出。

    • 使用 mockChooser 对象启动 App。

    • 按下按钮并验证测试调用 chooseFile 方法,并且标签指示没有选择文件。

classdef LaunchAppTest < matlab.uitest.TestCase & matlab.mock.TestCase
    methods (Test)
        function testInput(testCase)
            import matlab.mock.actions.AssignOutputs
            filename = 'input.txt';

            [mockChooser,behavior] = testCase.createMock(?FileChooser);
            when(behavior.chooseFile("*.*"),AssignOutputs(filename,pwd,1))

            app = launchApp(mockChooser);
            testCase.addTeardown(@close,app.UIFigure)

            testCase.press(app.Button)

            testCase.verifyEqual(app.Label.Text,filename)
        end

        function testCancel(testCase)
            import matlab.mock.actions.AssignOutputs

            [mockChooser,behavior] = testCase.createMock(?FileChooser);
            when(behavior.chooseFile("*.*"),AssignOutputs('input.txt',pwd,0))

            app = launchApp(mockChooser);
            testCase.addTeardown(@close,app.UIFigure)

            testCase.press(app.Button)

            testCase.verifyCalled(behavior.chooseFile("*.*"))
            testCase.verifyEqual(app.Label.Text,'No file selected')
        end
    end
end

运行测试。测试运行完成,无需手动选择文件。

runtests("LaunchAppTest");
Running LaunchAppTest
..
Done LaunchAppTest
__________

另请参阅

相关主题