Write Test That Uses App Testing and Mocking Frameworks

This example shows how to write a test that uses the app testing framework and the mocking framework. The app contains a file selection dialog box and a label indicating the selected file. To test the app programmatically, use a mock object to define the behavior of the file selector.

Create App

Create the launchApp app in your current working folder. The app allows a user to select an input file and displays the name of the file in the app. The file selection dialog box is a blocking modal dialog box that waits for user input.

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

To explore the properties of this app prior to testing, create an instance of the app at the command prompt. This step is not necessary for the tests, but it is helpful to explore the properties used by the app tests. For example, use app.Button to access the Input file button within the app object.

app = launchApp;

App window displaying Input file button

Test App With Manual Intervention

Create the LaunchAppTest class without using mocks. The test assumes the file input2.txt exists in your current working folder. If it does not exist, create it. The test presses the Input file button programmatically and verifies that the label matches 'input2.txt'. You must manually select the file.

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

Run the test. When the file selection dialog box appears, select input2.txt to allow MATLAB to proceed with the test. Selecting any other file results in a test failure.

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

Create Fully Automated Test

To test the app without manual intervention, use the mocking framework. Modify the app to accept a file-choosing service instead of implementing it in the app (dependency injection).

Create a FileChooser service with an Abstract method that implements the file selection functionality.

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

Create a default FileChooser that uses the uigetfile function for file selection.

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

Change the app to accept an optional FileChooser object. When called with no inputs, the app uses an instance of 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

Make the following modifications to LaunchAppTest.

  • Change the test to inherit from both matlab.uitest.TestCase and matlab.mock.TestCase.

  • Remove the properties block and the TestClassSetup block. Because the mock defines the output of the chooseFile method call, the test does not rely on the existence of an external file.

  • Change the testInputButton test method so that it will do these things.

    • Create a mock object of the FileChooser.

    • Define mock behavior such that when the chooseFile method is called with the input '*.*', the outputs are the test file name ('input2.txt'), the current working folder, and a selected filter index of 1. These outputs are analogous to the outputs from the uigetfile function.

    • Press the button and verify the selected file name. These steps are the same as in the original test, but the mock assigns the output values, so you do not need to interact with the app to continue testing.

  • To test the Cancel button, add a test method testInputButton_Cancel so that it will do these things.

    • Create a mock object of the FileChooser.

    • Define mock behavior such that when the chooseFile method is called with the input '*.*', the outputs are the test file name ('input2.txt'), the current working folder, and a selected filter index of 0. These outputs are analogous to the outputs from the uigetfile function if a user selects a file and then chooses to cancel.

    • Press the button and verify that the test calls the chooseFile method and that the label indicates that no file was selected.

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

Run the tests. The tests run to completion without manual file selection.

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

See Also

|

Related Topics