matlab

Matlab: test a class that takes user keyboard input


I would like to test a class GetInput that gets keyboard input from the user. How can my test class provide simulated keyboard input to GetInput? Here's the class to be tested.

classdef GetInput

    properties
        Answer
    end

    methods
        function obj = GetInput
            obj.Answer = input("Your answer? ","s");
        end
    end
end

And here's the test class.

classdef testGetInput < matlab.unittest.TestCase

    methods (Test)

        function tInput(testCase)
            g = GetInput();    
            myInput = 'abcdef';
            % I want to provide input automatically at this point   
            verifyEqual(testCase,g.Answer,myInput)
        end
    end
end

This all works, but only if I manually type in "abcdef" at the command window prompt. I want a way to automate the keyboard input.


Solution

  • Here's how you can do it. It's a bit roundabout. Start with an input interface class:

    classdef (Abstract) InputInterface
        methods
            inp = getInput(inputGetter,prompt)
        end
    end
    

    Create a default input class KeyboardInput that inherits from the abstract interface:

    classdef KeyboardInput < InputInterface
        methods
            function inp = getInput(~,prompt) % 1st input arg is InputInterface obj
                inp = input(prompt,"s");
            end
        end
    end
    

    Add a class UserQuery that uses one of these input classes:

    classdef UserQuery < handle
    
        properties
            InputSource
            Response
        end
    
        methods
            function obj = UserQuery(inputSrc)
                arguments
                    inputSrc (1,1) InputInterface = KeyboardInput
                end
                obj.InputSource = inputSrc;
            end
    
            function ask(obj,prompt)
                obj.Response = obj.InputSource.getInput(prompt);
            end
        end
    end
    

    Now test UserQuery with a mock:

    classdef tUserQuery < matlab.mock.TestCase
    
        properties
            Mock
            Behavior
            Asker
        end
    
        methods (Test)
            function testInput(tc)
                userSaid = "whatUserSaid";
                when(tc.Behavior.getInput(99), ...
                    matlab.mock.actions.AssignOutputs(userSaid))
                tc.Asker.ask(99)            
                tc.verifyTrue(isequal(tc.Asker.Response,userSaid))
            end
        end
    
        methods (TestClassSetup)
            function setupMock(tc)
                [tc.Mock,tc.Behavior] = tc.createMock(?InputInterface);
                tc.Asker = UserQuery(tc.Mock);
            end
        end
    
    end    
    

    The mock inherits from the abstract InputInterface, so the UserQuery accepts it as an input argument. The mock behavior is set up so that when it's called with input argument 99, it outputs "whatUserSaid".

    >> q = UserQuery(KeyboardInput);
    >> q.ask("Your answer? ")
    Your answer? yes
    >> q
    
    q = 
    
      UserQuery with properties:
    
        InputSource: [1×1 KeyboardInput]
           Response: 'yes'
    
    >>