Main Content

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

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

创建 App

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

function app = launchApp
    f = uifigure;
    button = uibutton(f,'Text','Input file');
    button.ButtonPushedFcn = @(src,evt)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 an App struct for output
    app.UIFigure = f;
    app.Button = button;
    app.Label = label;
    
    function file = pickFile()
        [file,folder,status] = uigetfile('*.*');
        if status
            label.Text = file;
        end
    end
end

要在测试之前了解此 App 的属性,请在命令提示符下创建该 App 的实例。此步骤对于测试不是必需的,但是了解 App 测试使用的属性会很有帮助。例如,使用 app.Button 访问 App 对象内的 Input file 按钮。

app = launchApp;

App window displaying Input file button

在手动干预下测试 App

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

classdef LaunchAppTest < matlab.uitest.TestCase
    properties
        TestFile = 'input2.txt';
    end
    methods(TestClassSetup)
        function checkTestFiles(tc)
            import matlab.unittest.constraints.IsFile
            tc.assumeThat(tc.TestFile,IsFile)
        end
    end
    methods (Test)
        function testInputButton(tc)
            app = launchApp;
            tc.addTeardown(@close,app.UIFigure);
            
            tc.press(app.Button);

            tc.verifyEqual(app.Label.Text,tc.TestFile)
        end
    end
end

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

results = 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(chooser,varargin)
            [file,folder,status] = uigetfile(varargin{:});
        end
    end
end

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

function app = launchApp(fileChooser)
    if nargin==0
        fileChooser = DefaultFileChooser;
    end
    f = uifigure;
    button = uibutton(f,'Text','Input file');
    button.ButtonPushedFcn = @(src,evt)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 an App struct for output
    app.UIFigure = f;
    app.Button = button;
    app.Label = label;
    
    function file = pickFile(fileChooser)
        [file,folder,status] = fileChooser.chooseFile('*.*');
        if status
            label.Text = file;
        end
    end
end

LaunchAppTest 进行以下修改。

  • 将测试更改为同时从 matlab.uitest.TestCasematlab.mock.TestCase 继承。

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

  • 更改 testInputButton 测试方法,以便它执行下列操作。

    • 创建 FileChooser 的一个 mock 对象。

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

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

  • 要测试 Cancel 按钮,请添加测试方法 testInputButton_Cancel,以便它执行下列操作。

    • 创建 FileChooser 的一个 mock 对象。

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

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

classdef LaunchAppTest < matlab.uitest.TestCase & matlab.mock.TestCase
    methods (Test)
        function testInputButton(tc)
            import matlab.mock.actions.AssignOutputs
            fname = 'myFile.txt';
            
            [mockChooser,behavior] = tc.createMock(?FileChooser);
            when(behavior.chooseFile('*.*'),AssignOutputs(fname,pwd,1))
            
            app = launchApp(mockChooser);
            tc.addTeardown(@close,app.UIFigure);
            
            tc.press(app.Button);

            tc.verifyEqual(app.Label.Text,fname);
        end
        
        function testInputButton_Cancel(tc)
            import matlab.mock.actions.AssignOutputs
            
            [mockChooser, behavior] = tc.createMock(?FileChooser);
            when(behavior.chooseFile('*.*'),AssignOutputs('myFile.txt',pwd,0))
            
            app = launchApp(mockChooser);
            tc.addTeardown(@close,app.UIFigure);
            
            tc.press(app.Button);
            
            tc.verifyCalled(behavior.chooseFile('*.*'));
            tc.verifyEqual(app.Label.Text,'No file selected');
        end
    end
end

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

results = runtests('LaunchAppTest');
Running LaunchAppTest
..
Done LaunchAppTest
__________

另请参阅

|

相关主题