Matlab UnitTest TestCase with optional plots of results? - matlab

While testing objects and functions of a project in Matlab using matlab.unittest.TestCase classes (new to 2013a), sometimes a plot is needed to visualise the actual/expected data.
I have so far used the following method but feel this isn't the best way:
classdef test1 < matlab.unittest.TestCase
properties
var1=3; var2=5; % sample variables
graph_output = 0; % boolean
end
methods(Test)
function testfunction(testCase)
my_result = my_fun(testCase.var1, testCase.var2);
testCase.verifyEqual(my_result,expected_result)
if testCase.graph_output
plot(my_result)
end
end
end
In the command line, I use test_obj=test1 and then test_obj.graph_output=1 before using run(test_obj) to output graphs as well as testing the function.
A better way to do this would be to use a separate method. I have tried this by allocating my_result to the properties list, but after the test completes, matlab seems to re-initialise my_result making the output graph meaningless.
Does anyone know a way round this, or any better way of outputting test results on demand?

Update: This is now much more streamlined in R2017a with the inclusion of FigureDiagnostic and ScreenshotDiagnostic. Check them out before going too far down this path!
Original Answer
You can do this not only for failing conditions but also for passing conditions with a combination of custom diagnostics and the DiagnosticsValidationPlugin. You can do this quickly using a function handle, but if this is something you find you want to do often for many of your tests, consider creating your own subclass of Diagnostic:
classdef PlotDiagnostic < matlab.unittest.diagnostics.Diagnostic
properties
Title
Actual
Expected
end
methods
function diag = PlotDiagnostic(title, actual, expected)
diag.Title = title;
diag.Actual = actual;
diag.Expected = expected;
end
function diagnose(diag)
diag.DiagnosticResult = sprintf('Generating plot with title "%s"', diag.Title);
f = figure('Title', diag.Title);
ax = axes('Parent', f);
plot(ax, 1:numel(diag.Actual), diag.Actual, 'r', 1:numel(diag.Expected), diag.Expected','b');
end
end
end
Then you can have a test that uses this like so:
classdef FooTest < matlab.unittest.TestCase
methods(Test)
function testFails(testCase)
actual = 1:10;
expected = fliplr(actual);
testCase.verifyEqual(actual, expected, PlotDiagnostic('Title1', actual, expected));
end
function testPasses(testCase)
actual = 1:10;
expected = actual;
testCase.verifyEqual(actual, expected, PlotDiagnostic('Title2', actual, expected));
end
end
end
Now once you have those as test diagnostics you will see them in failure conditions. However, you can also see them in passing conditions using the DiagnosticsValidationPlugin, which evaluates diagnostics even in passing conditions to ensure the diagnostic code is bug free (it would be super lame to not catch diagnostic info from a real failure because there was a bug in the diagnostic code that is typically not exercised). This would look like:
>> import matlab.unittest.*;
>> runner = TestRunner.withNoPlugins;
>> runner.addPlugin(matlab.unittest.plugins.DiagnosticsValidationPlugin);
>> suite = TestSuite.fromClass(?FooTest);
>> runner.run(suite)
Note that as of R2014a you can write your own plugin to listen to these passing diagnostics instead of using the DiagnosticsValidationPlugin. Really in this example we are not using this plugin with the intent to validate that the diagnostics are bug free, so it would be better to write a custom plugin with this specific purpose in mind.
Also, in R2014b you can leverage the log method to hook this up to a different dial. If' you'd like you can call:
testCase.log(Verbosity.Detailed, PlotDiagnostic('Title2', actual, expected));
Then you only see the plots when you are using a LoggingPlugin at the "Detailed" verbosity level, for example.

Usually when people are interested in looking at results from a specific test it's because something has gone wrong. This is a good opportunity to use custom diagnostics. Here is one that prints out a link to the MATLAB command window which plots the expected value against the actual value, as well as printing out links which will load the data from the test into the workspace.
classdef test1 < matlab.unittest.TestCase
methods(Test)
function firstTest(testCase)
import matlab.unittest.constraints.IsEqualTo;
% Test should pass!
actualValue = 1:10;
expectedValue = 1:10;
diagnostic = #()myPlotDiagnostic(actualValue, expectedValue);
testCase.verifyThat(actualValue, IsEqualTo(expectedValue), diagnostic);
end
function secondTest(testCase)
import matlab.unittest.constraints.IsEqualTo;
% Test should fail with a diagnostic!
actualValue = [1 2 3 4 12 6 7 8 9 10];
expectedValue = 1:10;
diagnostic = #()myPlotDiagnostic(actualValue, expectedValue);
testCase.verifyThat(actualValue, IsEqualTo(expectedValue), diagnostic);
end
function thirdTest(testCase)
import matlab.unittest.constraints.IsEqualTo;
% Test should also fail with a diagnostic!
actualValue = [1 2 3 4 -12 6 7 8 9 10];
expectedValue = 1:10;
diagnostic = #()myPlotDiagnostic(actualValue, expectedValue);
testCase.verifyThat(actualValue, IsEqualTo(expectedValue), diagnostic);
end
end
end
function myPlotDiagnostic(actualValue, expectedValue)
temporaryFile = tempname;
save(temporaryFile, 'actualValue', 'expectedValue');
fprintf('Plot Data\n', num2str(expectedValue), num2str(actualValue));
fprintf('Load data into workspace\n', temporaryFile);
end
Running this test will result in outputs which contain
Links which will load the actual and expected values into the workspace
Links which when clicked will produce the following graphs:
These will of course only show up though if the test fails, but this is generally the desired behaviour anyway!
N.B. I prefer to use the IsEqualTo syntax so that the tests read (almost) like English. But this is a style decision.

Related

can a matlab function detect if it has been called with semicolon [duplicate]

In a Matlab script I call a user-defined function (m-function). My function returns a value while printing the value to the command window using disp and/or fprintf calls.
When writing an expression or a statement, one puts ; at its end to suppress printing. When the expression calls my function, the ; can suppress the printing of the returned value. However, this does not effect the disp output from within the function called.
I want to eliminate the display output of the function when appropriate. Is there a way to determine whether a function call was made in an expression ending with ;?
I like the spirit of what you're trying to do, but I think that it probably goes against the common programming patterns in Matlab. As you correctly state, the purpose of the terminating semicolon is to supress printing of returned values. Trying to get it to incorporate your other feature might well require some deep hacking and ugly hard-to-maintain code. The standard way to implement what you describe is via property name-value pair arguments. For example, Matlab's optimization suite has a property called 'Display' that can be set to various values to indicate the desired level of verbosity (see optimset).
If you want to try looking for a way to check for terminating semicolons, you might look into the undocumented mint, mlintmex, and mtree functions – read more here. Unfortunately, using mlint to simply check for the "Terminate statement with semicolon to suppress output" warning (see this function on the MatlabCental File Exchange) won't work in all cases as a function call by itself doesn't produce this warning.
Update
Here's an attempt at code that could be inserted into a called function to determine if the line of the caller is terminated by a semicolon. You should be aware that this has not been thoroughly tested and is likely very fragile. I have not tested it with sub-functions or anonymous functions and I know that it fails if you wrap a line over multiple lines using ....
st = dbstack('-completenames'); % M-file of caller and line number
caller = st(2);
str = mlint('-lex',caller.file); % Struct containing parsed data
isSemicolon = false; % Assume no semicolon
for i = 1:length(str)
% Find end-of-line corresponding to function call
c = strsplit(str(i).message,'/');
if str2double(c{1}) == caller.line && strcmp(c{2}(end-5:end-1),'<EOL>')
% Check for comments
j = i-1;
if ~isempty(strfind(str(j).message,'%'))
j = j-1;
end
% Check for semicolon
if strcmp(str(j).message(end-2),';')
isSemicolon = true; % Semicolon found
break;
end
end
end
In other words, play around with this to learn, but I would not recommend actually using it.
I'm afraid that the answer to your question is no. That information is simply not passed on to the function being called.
You shouldn't think about adding the semicolon as a means to "suppress printing", but rather that the lack of a semicolon instructs MATLAB to call the display function on the output variables of the function call. In other words, MATLAB interprets this code:
y = myFunc(x)
as:
y = myFunc(x);
display(y);
I think adding a 'print' or 'verbose' parameter to your function is your best bet for achieving what you want.
I think the simplest method to achieve the results you want (i.e. whether or not disp's get displayed) is to add an extra function input or output. For example, adding an input (optional, you can set default behaviour):
function y=myFunc(a,displayResults)
if nargin==1
displayResults=true; %// set the default behaviour
end
%// if you want to print something
if displayResults
disp(a)
end
end
Or an extra output. In this case foo produces no output to the screen, but all the messages are saved into a cell array, which can be accessed if desired:
function [x,m] = foo(a)
m={}; %// initialise cell array of output messages
x=a;
m{length(m)+1}=a; %// some message
m{length(m)+1}='another message'; %// another message
end
I think the first option will be better, the second will not deal with fprintf well, and displaying elements of m could be tricky depending on what it contains. The first method is very simple, and does not even require you to change existing code, as you can make the displayResults input optional and set the default to be what you want.
You may suppress disp outputs by locally redefining it at the beginning of the function:
function [] = hideDisplay()
%[
% Override `disp` behavior
disp = #(x)doNothing;
% Next `disp` calls will no longer appear in matlab console
disp('Hello')
disp('World!')
%]
end
%% ---
function [] = doNothing()
%[
%]
end
Here's a possible workaround (to be clear - this is not a real answer to the question per-se, just a way to avoid the unwanted behaviour). Say my function is:
function y = prt_test(x)
y = x + 1;
disp('IN PRT_TEST')
end
Calling:
>> % Regular use - message and output are displayed:
>> y = prt_test(1)
IN PRT_TEST
y =
2
>> % Regular use w/ ";" - only message is displayed:
>> y = prt_test(2);
IN PRT_TEST
>> % Use "evalc()" - message and output are displayed:
>> evalc('y = prt_test(3)')
ans =
IN PRT_TEST
y =
4
>> % Use "evalc()" w/ func ";" - only message is displayed:
>> evalc('y = prt_test(4);')
ans =
IN PRT_TEST
>> % Use "evalc();" - no output:
>> evalc('y = prt_test(5)');
>> % Use "evalc();" w/ func ";" - no output:
>> evalc('y = prt_test(6);');
>>

Looping a Function in Matlab

total newbie here. I'm having problems looping a function that I've created. I'm having some problems copying the code over but I'll give a general idea of it:
function[X]=Test(A,B,C,D)
other parts of the code
.
.
.
X = linsolve(K,L)
end
where K,L are other matrices I derived from the 4 variables A,B,C,D
The problem is whenever I execute the function Test(1,2,3,4), I can only get one answer out. I'm trying to loop this process for one variable, keep the other 3 variables constant.
For example, I want to get answers for A = 1:10, while B = 2, C = 3, D = 4
I've tried the following method and they did not work:
Function[X] = Test(A,B,C,D)
for A = 1:10
other parts of the code...
X=linsolve(K,L)
end
Whenever I keyed in the command Test(1,2,3,4), it only gave me the output of Test(10,2,3,4)
Then I read somewhere that you have to call the function from somewhere else, so I edited the Test function to be Function[X] = Test(B,C,D) and left A out where it can be assigned in another script eg:
global A
for A = 1:10
Test(A,2,3,4)
end
But this gives an error as well, as Test function requires A to be defined. As such I'm a little lost and can't seem to find any information on how can this be done. Would appreciate all the help I can get.
Cheers guys
I think this is what you're looking for:
A=1:10; B=2; C=3; D=4;
%Do pre-allocation for X according to the dimensions of your output
for iter = 1:length(A)
X(:,:,iter)= Test(A(iter),B,C,D);
end
X
where
function [X]=Test(A,B,C,D)
%other parts of the code
X = linsolve(K,L)
end
Try this:
function X = Test(A,B,C,D)
% allocate output (it is faster than changing the size in every loop)
X = {};
% loop for each position in A
for i = 1:numel(A);
%in the other parts you have to use A(i) instead of just A
... other parts of code
%overwrite the value in X at position i
X{i} = linsolve(K,L);
end
end
and run it with Test(1:10,2,3,4)
To answer what went wrong before:
When you loop with 'for A=1:10' you overwrite the A that was passed to the function (so the function will ignore the A that you passed it) and in each loop you overwrite the X calculated in the previous loop (that is why you can only see the answer for A=10).
The second try should work if you have created a file named Test.m with the function X = (A,B,C,D) as the first code in the file. Although the global assignment is unnecessary. In fact I would strongly recommend you not to use global variables as it gets very messy very fast.

How to determine if a function was called followed by a semicolon (";")?

In a Matlab script I call a user-defined function (m-function). My function returns a value while printing the value to the command window using disp and/or fprintf calls.
When writing an expression or a statement, one puts ; at its end to suppress printing. When the expression calls my function, the ; can suppress the printing of the returned value. However, this does not effect the disp output from within the function called.
I want to eliminate the display output of the function when appropriate. Is there a way to determine whether a function call was made in an expression ending with ;?
I like the spirit of what you're trying to do, but I think that it probably goes against the common programming patterns in Matlab. As you correctly state, the purpose of the terminating semicolon is to supress printing of returned values. Trying to get it to incorporate your other feature might well require some deep hacking and ugly hard-to-maintain code. The standard way to implement what you describe is via property name-value pair arguments. For example, Matlab's optimization suite has a property called 'Display' that can be set to various values to indicate the desired level of verbosity (see optimset).
If you want to try looking for a way to check for terminating semicolons, you might look into the undocumented mint, mlintmex, and mtree functions – read more here. Unfortunately, using mlint to simply check for the "Terminate statement with semicolon to suppress output" warning (see this function on the MatlabCental File Exchange) won't work in all cases as a function call by itself doesn't produce this warning.
Update
Here's an attempt at code that could be inserted into a called function to determine if the line of the caller is terminated by a semicolon. You should be aware that this has not been thoroughly tested and is likely very fragile. I have not tested it with sub-functions or anonymous functions and I know that it fails if you wrap a line over multiple lines using ....
st = dbstack('-completenames'); % M-file of caller and line number
caller = st(2);
str = mlint('-lex',caller.file); % Struct containing parsed data
isSemicolon = false; % Assume no semicolon
for i = 1:length(str)
% Find end-of-line corresponding to function call
c = strsplit(str(i).message,'/');
if str2double(c{1}) == caller.line && strcmp(c{2}(end-5:end-1),'<EOL>')
% Check for comments
j = i-1;
if ~isempty(strfind(str(j).message,'%'))
j = j-1;
end
% Check for semicolon
if strcmp(str(j).message(end-2),';')
isSemicolon = true; % Semicolon found
break;
end
end
end
In other words, play around with this to learn, but I would not recommend actually using it.
I'm afraid that the answer to your question is no. That information is simply not passed on to the function being called.
You shouldn't think about adding the semicolon as a means to "suppress printing", but rather that the lack of a semicolon instructs MATLAB to call the display function on the output variables of the function call. In other words, MATLAB interprets this code:
y = myFunc(x)
as:
y = myFunc(x);
display(y);
I think adding a 'print' or 'verbose' parameter to your function is your best bet for achieving what you want.
I think the simplest method to achieve the results you want (i.e. whether or not disp's get displayed) is to add an extra function input or output. For example, adding an input (optional, you can set default behaviour):
function y=myFunc(a,displayResults)
if nargin==1
displayResults=true; %// set the default behaviour
end
%// if you want to print something
if displayResults
disp(a)
end
end
Or an extra output. In this case foo produces no output to the screen, but all the messages are saved into a cell array, which can be accessed if desired:
function [x,m] = foo(a)
m={}; %// initialise cell array of output messages
x=a;
m{length(m)+1}=a; %// some message
m{length(m)+1}='another message'; %// another message
end
I think the first option will be better, the second will not deal with fprintf well, and displaying elements of m could be tricky depending on what it contains. The first method is very simple, and does not even require you to change existing code, as you can make the displayResults input optional and set the default to be what you want.
You may suppress disp outputs by locally redefining it at the beginning of the function:
function [] = hideDisplay()
%[
% Override `disp` behavior
disp = #(x)doNothing;
% Next `disp` calls will no longer appear in matlab console
disp('Hello')
disp('World!')
%]
end
%% ---
function [] = doNothing()
%[
%]
end
Here's a possible workaround (to be clear - this is not a real answer to the question per-se, just a way to avoid the unwanted behaviour). Say my function is:
function y = prt_test(x)
y = x + 1;
disp('IN PRT_TEST')
end
Calling:
>> % Regular use - message and output are displayed:
>> y = prt_test(1)
IN PRT_TEST
y =
2
>> % Regular use w/ ";" - only message is displayed:
>> y = prt_test(2);
IN PRT_TEST
>> % Use "evalc()" - message and output are displayed:
>> evalc('y = prt_test(3)')
ans =
IN PRT_TEST
y =
4
>> % Use "evalc()" w/ func ";" - only message is displayed:
>> evalc('y = prt_test(4);')
ans =
IN PRT_TEST
>> % Use "evalc();" - no output:
>> evalc('y = prt_test(5)');
>> % Use "evalc();" w/ func ";" - no output:
>> evalc('y = prt_test(6);');
>>

Matlab R2014a - defining a variable after a function is called

In a matlab script, is it possible to define a variable after the function is called?
(i.e. the script finds the function with the undefined variable, looks for the variable initialization and executes the function afterwards)
I need this because I have a large number of variables and functions and I don't want to order them in a sequential way but in the way that is appropriate for my problem.
E.g.
mean(x);
x = [1, 2, 3];
Thanks
Disclaimer: I think what you want to do is a really bad practice. If you find yourself in the need to do this, your code screams refactoring.
Yes, it is possible. There are two ways.
Using the try-catch statement:
try
x_mean = mean(x)
catch exception
if isequal(exception.identifier, 'MATLAB:UndefinedFunction')
x = [1,2,3];
x_mean = mean(x);
else
rethrow(exception);
end
end
The explanation: if the mean(x) fails, the code will enter in the catch branch. Then it verifies that the error is happening because a missing variable and if that's the case, the variable x is defined and the function re-run. There is a caveat: Matlab uses the same identifier for missing variable and for missing function.
Using the exist function:
if exist('x') == 1 %// the function have several return values
x_mean = mean(x);
else
x = [1, 2, 3];
x_mean = mean(x);
end
The explanation: the exist function will verify if a variable with the name x exists in the workspace. If it does exist, it will return 1 and you can execute your function. If it does not exist, then you can declare it first and then run mean. See documentation here.
My two cents: once again, this is a horrible way to achieve things. By going this way you will end up with pure spaghetti code. It will be a nightmare to debug. It will be a nightmare to maintain. There is a 99% chance that there are better ways to do what you want. It is just a matter of investing time and think a little bit. Even if it is a personal project or a quick script to solve a homework/task, you will learn much more by doing it the right way.
If I understand you question correctly, then yes it can be done by some ugly hacks.
The following class can be used:
classdef AdvancedFunctionHandle
properties(GetAccess = 'private', SetAccess = 'private')
handle
variables
end
methods
function obj = AdvancedFunctionHandle(fct, varargin)
if ~isa(fct,'function_handle')
error('The function must be a function handle!');
end
argins = numel(varargin);
variables = cell(1,argins);
for i = 1:argins
if ~ischar(varargin{i})
error('The variables must be represented by a string variabel name!');
end
if (~isvarname(varargin{i}) )
error('The variables must be a string representing a legal variabel name!');
end
variables{i} = varargin{i};
end
obj.handle = fct;
obj.variables = variables;
end
function val = subsindex(obj)
val = obj.calculate();
end
function val = calculate(obj)
try
vars = cell(1,numel(obj.variables));
for i = 1:numel(obj.variables)
v = evalin('base',obj.variables{i});
if isa(v,'AdvancedFunctionHandle')
vars{i} = v.calculate();
else
vars{i} = v;
end
end
val = obj.handle(vars{:});
catch
val = obj;
end
end
function display(obj)
disp([func2str(obj.handle),'(',strjoin(obj.variables,','),')']);
end
end
end
I made a small test script for it too:
clear
m = AdvancedFunctionHandle(#mean,'x');
s = AdvancedFunctionHandle(#(n)sum(1:n),'m');
m.calculate
x = [1,2,3];
s.calculate
I hope this solves your problem.
Notice that since the class refers to the base workspace, it will not work when run from a function, or if the script it is run from is run from a function.
I don't understand what you are trying to achieve, but if you want to define things after they are used, I think they have to be functions.
Example file:
function myfun()
mean(getX)
function X = getX
X = [1 2 3]
So though it is technically possible to define variables after they are used, you would really not want to do that.

Matlab functionality similar to python's __name__=='__main__'

I am looking for Matlab functionality to differentiate when script is run directly or from another script.
I build a script where I declare data to work on and I use this on other scripts and functions. When I run this script directly I would like to plot these data. When I call this script from another script I don't want to have all those plots.
In python I can build a function for plottings and call this function only when __name__=='__main__' I can't find how to do that in Matlab.
As example:
data.m
a = [1 2 3 4 5]
b = sin(a)
% plot only if run directly
figure
plot(a,b)
analysis.m
data
c = a.^2
figure
plot(c)
When I run analysis.m I want to have only plot(c) but not any other.
You can use ´dbstack´ [1] to see the function calls.
To complement #tashuhka answer (i.e using dbstack), and depending if you want to keep variables in the global scope, another solution is to turn your script into function and pass optional parameter to 'analysis.m'.
function [] = foo(doDebugPlot)
%[
% Check params
if (nargin < 1), doDebugPlot = true; end
% Code
...
% Debug
if (~doDebugPlot), return; end
plot(lala);
plot(tutut);
%]
I don't know if this is possible in MATLAB. A workaround would be to use an if together with exist, like this:
analysis.m
run_data = 1;
data
c = a.^2
figure
plot(c)
data.m
a = [1 2 3 4 5]
b = sin(a)
% plot only if run directly
if ~exist('run_data','var')
figure
plot(a,b)
end