Code covered by the BSD License  

Highlights from
mlunit_2008a

image thumbnail
from mlunit_2008a by Christopher
A MATLAB unit test framework supporting new classdef files (r2008a)

TestCase
classdef TestCase < handle
    % TESTCASE Superclass for user test case classes
    %
    % This class is part of the mlunit_2008a testing framework.
    %
    % To create tests, you create a test class by subclassing TestCase and
    % adding test methods.  Test methods are any methods with names
    % beginning with "test". The testing framework automatically finds and
    % runs these methods for you.
    % 
    % Within your test functions you can run whatever arbitrary code you
    % wish, and validate results using one of the inherited assert methods
    % described below.
    % 
    %   assert(obj, expr, [fail_message])
    %     Succeeds if expr is true, otherwise records a failure.
    % 
    %   assertEquals(obj, expected, actual, [fail_message])
    %     Succeeds if actual equals expected, otherwise records a failure. 
    %     Equality is determined using the MATLAB isequal() function, and
    %     works on pretty much any MATLAB types.
    % 
    %   assertNotEquals(obj, a, b, [fail_message])
    %     Suceeds if ~isequal(a,b), and otherwise records a failure.
    %
    % The optional fail_message parameter in each of these methods should
    % contain a string which will be added to the test report if the assert
    % fails.
    % 
    % You can also run code that throws a catchable exception; the test
    % framework will catch exceptions and record them as errors. You can
    % also perform your own custom validations, and record errors using the
    % fail() method:
    % 
    %   fail(obj, message)
    %     Always records a failure.
    %
    % Sometimes it may be desirable to initialize some object (typically
    % stored in a class property) with a known starting state, for instance
    % opening a file or constructing a matrix.
    % 
    % Rather than performing this operation yourself in each test method,
    % you can override the inherited setUp method to do it for you.  The
    % setUp method is automatically run before every invocation of a test
    % method.  Similarly, tearDown is called after every test method,
    % and may be used to clean up as necessary.
    % 
    % The method signatures to override in your class:
    %   setUp(obj)
    %   tearDown(obj)

    properties
        stopRequested = false;
    end

    methods
    % you can/should override these methods
        function self = setUp(self), end
        function self = tearDown(self), end
    end

    % you can, but probably don't want to override these
    methods
        function s = getDescription(self)
            s = class(self);
        end
        
        function self = assert(self, expr, msg, internal_call)
            if nargin < 3, msg = 'Assert failure'; end
            if nargin < 4, internal_call = 1; end

            if ((isempty(expr)) || (~expr))
                self.fail(msg, internal_call + 1);
            end;
        end

        function self = assertEquals(self, expected, actual, msg, internal_call)
            if nargin < 5, internal_call = 1; end
            if nargin < 4
                s1 = self.toString(expected);
                s2 = self.toString(actual);
                msg = sprintf('Assert equals failure: \n expected:\n %s\n\n actual:\n %s', s1, s2);
            end

            self.assert(isequal(expected, actual), msg, internal_call + 1);
        end

        function self = assertNotEquals(self, a, b, msg, internal_call)
            if nargin < 5, internal_call = 1; end
            if nargin < 4
                s1 = self.toString(a);
                msg = sprintf('Assert not equals failure:\n both values ==\n %s\n', s1);
            end

            self.assert(~isequal(a, b), msg, internal_call + 1);
        end

        function fail(self, msg, internal_call)
            if nargin < 2, msg = 'Unspecified failure'; end
            if nargin < 3, internal_call = 1; end

            error(['MLUNIT:TESTFAIL:frame' num2str(internal_call)], msg);
        end
    end

    % don't fool around with these
    methods
        % use reflection to obtain test method names
        function names = getTestNames(self)
            c = class(self);
            fns = methods(c);

            % exclude incidentally matching constructors - users *should*
            % avoid this, but...
            exclude = strcmp(c, fns);
            fns = fns(~exclude,1);

            tests = strmatch('test', fns);
            names = fns(tests,1);
        end

        % instantiate a test result object on ourselves
        function tr = getNewTestResult(self, parent)
            if nargin < 2, parent = []; end
            tr = TestCaseResult(self, parent);
        end
                
        % the big kahuna
        function self = runTests(self, result)            
            names = self.getTestNames();
            for i = 1:length(names)
                drawnow
                if self.stopRequested
                    return;
                end
                name = names{i};
                try
                    self.setUp();
                    feval(name, self);
                    self.tearDown();
                    
                    result.recordSuccess(name);
                    
                catch ME
                    msg = ME.message;
                    stack = ME.stack;
                    msg_id = ME.identifier;

                    start_frame = 1;
                    if strncmp('MLUNIT:TESTFAIL', msg_id, 15)
                        start_frame = start_frame + str2num(msg_id(22:end));
                    end

                    result.recordFailure(name, msg, stack(start_frame:end));
                end
            end
        end
        
        function stopTests(self)
            self.stopRequested = true;
        end
        
        function clearStop(self)
            self.stopRequested = false;
        end

        % for printing out expected and actual objects in the assertEquals
        % method.  This will attempt to convert various types into a
        % reasonable string representation, and will truncate strings when
        % they get too long.
        function s = toString(self, x)
            if ischar(x)
                s = x;
            elseif isnumeric(x)
                s = mat2str(x);
            else
                [m,n] = size(x);
                c = class(x);
                if m > 1 || n > 1
                    s = ['A ' num2str(m) 'x' num2str(n) ' ' c ' array'];
                elseif any(strmatch(c(1), ['a';'e';'i';'o';'u';'A';'E';'I';'O';'U']))
                    s = ['An ' c];
                else
                    s = ['A ' c];
                end
            end

            if length(s) > 75
                s = [s(1:75) '...'];
            end
        end
    end
end

Contact us at files@mathworks.com