Using both strings and functions in Matlab UnitTest diagnostics? - matlab

Please refer to the documentation for the testCase.verifyEqual method here. The documentation says that only one of the diagnostic features can be used. My requirement is I need two diagnostics at the same time - strings and function handle. The following is simple example of what I'm trying to achieve,
classdef testArrays < matlab.unittest.TestCase
methods (Test)
function testArraysEquality(testCase)
a = 1:10;
b = 1:10;
incrementFunc = #(x)x+1;
failureCount;
for i=1:length(a)
testCase.verifyEqual(a(i),b(i),'AbsTol',10e-3,['Test failed array element# ' num2str(i) ' failure count ' num2str(incrementFunc(failureCount))]);
end
disp([num2str(failureCount) ' out of ' num2str(length(a)) ' test cases failed']);
end
end
end
The problem is Anonymous function don't store values. On the other hand with the 'assignin' feature shown below, the value can be incremented and stored, but cannot be returned for use inside disp(). Is there any work around for this?
incrementFunc1 = #(x) assignin('caller', inputname(1), x+1);

You can include more than one (as well as more than one type) of diagnostic in the MATLAB Unit Test Framework by simply providing a diagnostic array to verifyEqual. You can actually do this explicitly as follows:
import matlab.unittest.diagnostics.StringDiagnostic;
import matlab.unittest.diagnostics.FunctionHandleDiagnostic;
testCase.verifyEqual(a,e, [StringDiagnostic('some string'), FunctionHandleDiagnostic(#() someFunction)]);
However, the Diagnostic.join method is provided to make that easier:
import matlab.unittest.diagnostics.Diagnostic;
testCase.verifyEqual(a,e, Diagnostic.join('some string', #() someFunction));
In order to do the increment call you are probably going to want to add a failed listener to the testCase in order to increment properly. Note that people/plugins can actually add listeners and execute these diagnostics in passing cases in addition to failing cases. As such your diagnostic messages should not assume that every time they are invoked it is in a failure condition. This not only applies to your incrementing code but also to just the message you are providing. I would suggest that instead of saying:
Test failed array element# 3 failure count 2
you should say:
Tested array element# 3 failure count 2
The framework diagnostic will let you know whether it failed or not. Anyway, takeaway, don't rely on invoking the diagnostics to determine failure count. What then? Take a look at the Events section here. You should listen explicitly for verification failed events in order to add that information to your diagnostics.
For the first solution, I am not sure why you need to provide the failure count for every failure. It seems like that would be very verbose. If you don't need that then you can do something like this:
classdef testArrays < matlab.unittest.TestCase
methods (Test)
function testArraysEquality(testCase)
a = 1:10;
b = 1:10;
failureCount = 0;
testCase.addlistener('VerificationFailed', #incrementFailureCount);
function incrementFailureCount(varargin)
% This is a nested function & has the scope and can see/modify
% the failureCount variable. This could also be done with a
% property on the class ans a method that increments it
failureCount = failureCount + 1;
end
for i=1:length(a)
testCase.verifyEqual(a(i),b(i),'AbsTol',10e-3,['Tested array element # ' num2str(i)]);
end
% I suggest using log instead of disp. If you want it to show up most of the time you can
% log it at Terse (1) verbosity. However, if you don't want to see it you can turn it off.
testCase.log(1, sprintf('%d out of %d test cases failed', failureCount, length(a)));
end
end
end
Is that good enough? If you really want to show the failure count in the diagnostics for each failure you can its just a bit more complicated and requires another nested function (or property access).
classdef testArrays < matlab.unittest.TestCase
methods (Test)
function testArraysEquality(testCase)
import matlab.unittest.diagnostics.Diagnostic;
a = 1:10;
b = 1:10;
failureCount = 0;
testCase.addlistener('VerificationFailed', #incrementFailureCount);
function incrementFailureCount(varargin)
failureCount = failureCount + 1;
end
function displayFailureCount
fprintf(1, 'Failure Count: %d', failureCount);
end
for i=1:length(a)
testCase.verifyEqual(a(i),b(i),'AbsTol',10e-3, ...
Diagnostic.join(...
['Tested array element #' num2str(i)], ...
#displayFailureCount));
end
testCase.log(1, sprintf('%d out of %d test cases failed', failureCount, length(a)));
end
end
end
Does that help you accomplish what you are trying to do?

Related

Informative feedback from assert with matrices in Matlab

Does there exist a simple function in Matlab that will give more informative feedback when using assert with matrices than the simple application of the assert function?
My simple application is:
>> assert(all([1 2; 3 4] == [1 2; 3 5], 'all'))
Assertion failed.
In Python, with numpy.testing.assert_equal the feedback from a failed assertion shows the two arrays.
I guess it would be possible to define further the arguments to the assert function errmsg, value1 and value2.
assert is to validate intermediate values inside your code, so you get an error when something is not as you expect, and you can debug it. This is the “fail early” philosophy. You don’t need to get a detailed output here, it tells you that you need to break out the debugger.
Your use case seems closer to testing the output of a function, to verify it works as intended. This is a very different use case, for which MATLAB has the testing framework.
For example, your equality comparison would be implemented through verifyEqual:
testCase = matlab.unittest.TestCase.forInteractiveUse;
verifyEqual(testCase,A,B)
Here's one of the many ways assert can be used in conjunction with the try/catch and throw commands to catch errors and take specific actions (e.g. print a message and throw an exception that can be captured by the calling function:
function out = myfun(A, B)
out = 0;
try
assert(all(size(A)==size(B)), 'Matrix sizes do not match')
catch exc % contains the message passed by assert
fprintf('Size of A is: %d, %d\n',size(A)); % show the actual dimensions
fprintf('Size of B is: %d, %d\n',size(B));
out = 1;
throw(exc) % throws exc and returns control to the caller
end
try
assert(isequal(A,B), 'Matrix are not identical.')
catch exc % contains the message passed by assert
disp(A==B) % show 0 where elements don't match
out = 1;
throw(exc) % throws exc and returns control to the caller
end
end
Calling myfun with A=ones(4,4) and B=ones(4,5) produces the following output:
Calling myfun with A=ones(4,4) and B=2*ones(4,4) leads to:
As I mentioned at the very beginning, the function above represent one of the possible implementations.

MATLAB: How to automatically abort failed unit test

I am wondering how I can make a unit test to automatically stop, once an error in the system under test (sut) occurs?
Let's assume, the unit test runs some 1,000 different input combinations, and is supposed to verify that the results are equal to the expectations. Now, let's further assume there is a simple syntax error in sut, which causes an error. In this case I would like the unit test to automatically stop and invoke the tear down method.
Is that possible?
Edit:
I am borrowing a MATLAB example from their help site (https://de.mathworks.com/help/matlab/matlab_prog/create-basic-parameterized-test.html) in order to show more clearly what I mean:
Here you can see the test class:
classdef TestCarpet < matlab.unittest.TestCase
properties (TestParameter)
type = {'single','double','uint16'};
level = struct('small', 2,'medium', 4, 'large', 6);
side = struct('small', 9, 'medium', 81,'large', 729);
end
methods (Test)
function testRemainPixels(testCase, level)
% expected number pixels equal to 1
expPixelCount = 8^level;
% actual number pixels equal to 1
actPixels = find(sierpinski(level));
testCase.verifyNumElements(actPixels,expPixelCount)
end
function testClass(testCase, type, level)
testCase.verifyClass(...
sierpinski(level,type), type)
end
function testDefaultL1Output(testCase)
exp = single([1 1 1; 1 0 1; 1 1 1]);
testCase.verifyEqual(sierpinski(1), exp)
end
end
methods (Test, ParameterCombination='sequential')
function testNumel(testCase, level, side)
import matlab.unittest.constraints.HasElementCount
testCase.verifyThat(sierpinski(level),...
HasElementCount(side^2))
end
end
end
Here's the system under test:
function carpet = sierpinski(nLevels,classname)
if nargin == 1
classname = 'single';
end
% original line: mSize = 3^nLevels;
mSize = "That's clearly wrong here";
carpet = ones(mSize,classname);
cutCarpet(1,1,mSize,nLevels) % begin recursion
function cutCarpet(x,y,s,cL)
if cL
ss = s/3; % define subsize
for lx = 0:2
for ly = 0:2
if lx == 1 && ly == 1
% remove center square
carpet(x+ss:x+2*ss-1,y+ss:y+2*ss-1) = 0;
else
% recurse
cutCarpet(x + lx*ss, y + ly*ss, ss, cL-1)
end
end
end
end
end
end
I changed the definition of mSize to a string to produce an error. Now, if I run the tests, all tests will result in an error. I am wondering if it is possible to stop the tests as soon as possible, i.e. after the occurence of the first error?
The problem that I see is that the test code won't even reach the last line of testRemainPixels (testCase.verifyNumElements(actPixels,expPixelCount)). A fatalAssert does not help at this point, right?
Assuming you are using the test framework built into MATLAB, take a look at the types of qualifications, in particular the bulleted list at the top of this page:
https://www.mathworks.com/help/matlab/matlab_prog/types-of-qualifications.html
If you want to stop the entire testing session you can use fatal assertions. If you want other tests to continue you can use assertions. If you want to skip all the test methods (and parameterizations) in a single file you can use an assertion in TestClassSetup or just add a basic "smoke" level exercise step of the code in TestClassSetup and if it errors it will behave like an assertion.
Hope that helps.

How can I get the Methodlist while iterating?

I want to iterate through all classes and packages in a special path.
After that, I want to get the MethodList.
In the command window I can use following and it’s working fine:
a = ?ClassName;
a.MethodList(X);
Now I separate this into a function:
function s = befehlsreferenz(path)
s = what(path); % list MATLAB files in folder
for idx = 1:numel(s.classes)
c = s.classes(idx);
b = ?c;
b.MethodList(0);
end
end
I get an error:
Too many outputs requested. Most likely cause is missing [] around left hand side that has a comma separated list
expansion. Error in (line 7) b.MethodList(0);
While debugging I can see:
c: 1x1 cell = ‘Chapter’
b: empty 0x0 meta.class
Why is b empty? How can I get the methodlist?
1 Edit:
Here is an example class, also not working with it.
classdef TestClass
%TESTCLASS Summary of this class goes here
% Detailed explanation goes here
properties
end
methods
function [c] = hallo(a)
c = 1;
end
end
end
When struggling with operators in Matlab, it's typically the best choice to use the underlying function instead, which is meta.class.fromname(c)
Relevant documentation: http://de.mathworks.com/help/matlab/ref/metaclass.html
Further it seems s.classes(idx); is a cell, use cell indexing: s.classes{idx} ;

try/catch and error with empty string

I'm working with someone else's code and I am unfamiliar with try/catch so I made a small, similar example. On line 11, if I write error(''), it doesn't seem to catch the error and increase the index j. However, writing error(' ') or error('bad!') does.
So does having an error with an empty string ignore the error, or am I doing something wrong?
% Just a file to understand the Matlab command try/catch
M = 3;
j = 1;
k = [Inf, 5, 4];
while M>0
try
M = M-1
u = k(j)
if (isinf(u)||isnan(u)), error(''), end;
catch
j = j+1
end
end
Yes, error('') and error([]) and error(struct([])) all do not actually display an error message and abort running code. I personally consider the use of the single string argument version of error to be bad practice in any real code. You should use always use both a 'MSGID' and a 'ERRMSG' when writing errors for your functions, e.g.
error('FunctionName:SubFunctionName:ErrorMSGID','Error message to be printed.')
Alternatively, you can use MException objects in conjuction with throw, rethrow, and throwAsCaller, which allow you to reuse error information. More here.
It is odd, but it's in the documentation for error, for the error('msgString') syntax:
All string input arguments must be enclosed in single quotation marks. If msgString is an empty string, the error command has no effect.
Similarly, if using the error(msgStruct) syntax:
If msgStruct is an empty structure, no action is taken and error returns without exiting the function.
if you have a look to the try documentation you can have an example.
Else want you want for your code it :
M = 3;
j = 1;
k = [Inf, 5, 4];
while M>0
try
M = M-1
u = k(j)
if (isinf(u)||isnan(u)), error(''), end;
catch
disp('I catch an error!');
j = j+1
end
end
Because If you never get an error in your code, it will never go in the catch. So by including error('');, it just to say, go execute the statement in the catch.
But you can just modify your code by replacing the error() by the statements into your catch like this :
while M>0
M = M-1
u = k(j)
if (isinf(u)||isnan(u)), j = j+1, end;
end
EDIT
If you take a look in the documentation, you can found this :
% ERROR(MSGSTRUCT) reports the error using fields stored in the scalar
% structure MSGSTRUCT. This structure can contain these fields:
%
% message - Error message string
% identifier - See MESSAGE IDENTIFIERS, below
% stack - Struct similar to the output of the DBSTACK function
%
% If MSGSTRUCT is an empty structure, no action is taken and ERROR
% returns without exiting the program. If you do not specify the
% stack, the ERROR function determines it from the current file and line.
So no action is taken as you can read. And nothing, so catch don't get any informations.
Not sure why you need it, but here is how it works.
error function does not throw an error with empty string or empty vector ([]) as an argument.
If you don't specify argument at all the error function itself generates the error "Not enough arguments". So it will go to catch.
Another way is to specify an empty structure as an argument.
s = struct();
error(s)
In this case, the error will be generated, but the code will not stop and in general flow you will hear no beep. In your case it should go to catch.

How to write a function that does not throw a "wrong number of arguments" error

I am trying to write a minimal function that can be called with a variable number of arguments but that will not throw a wrong number of arguments error if miscalled.
Here is where I start from :
function varargout=fname(varargin)
% FNAME
% Usage: output=fname(input)
% Arguments check
if(nargin~=1 || nargout~=1)
disp('Function fname requires one input argument');
disp('and one output argument');
disp('Try `help fname`');
varargout(1:nargout)={0};
return;
end
input=varargin{1};
output=input;
varargout(1)={output};
end
However this does not work as I would like it to. Is there a way to write a function that :
never throw a "wrong number of arguments" error (so that the rest of the execution can continue)
accepts variable number of input and output arguments and checks them inside the function
(maybe more tricky) if the number of input / output arguments is not correct, does not replace the value of the provided output arguments (so that any misplaced call does not erase the previous value of the output argument)
I am open to any suggestions / other methods.
Thank you for your help.
UPDATE: thanks to #Amro for his answer, I guess what I miss here is either a call by address of reference for Matlab functions or a way to interrupt a function without returning anything and without stopping the rest of the execution.
Here is one way to implement your function:
function varargout = fname(input,varargin)
%# FNAME
%# Usage: output=fname(input)
%%# INPUT
if nargin<1
varargout(1:nargout) = {[]};
warning('Not enough input arguments.'), return
end
if ~isempty(varargin)
warning('Too many input arguments.')
end
%%# YOUR CODE: manipulate input, and compute output
output = input;
%%# OUTPUT
varargout{1} = output;
if nargout>1
warning('Too many output arguments.')
varargout(2:nargout) = {[]};
end
end
Obviously you can customize the warning messages to your liking...
Also, if you want your function to simply print the message instead of issuing warnings, replace all WARNING calls with simple DISP function calls.
Examples of function call:
fname()
fname(1)
fname(1,2)
x = fname()
x = fname(1)
x = fname(1,2)
[x,y] = fname()
[x,y] = fname(1)
[x,y] = fname(1,2)
The above calls execute as expected (showing warning messages when applicable). One caveat though, in the last three calls, if the variable y already existed in the workspace prior to the calls, it would be overwritten by the empty value y=[] in each...
If I understand your question correctly, then the answer is no. If a caller calls a function like this:
[a, b, c] = fname('foo');
then fname is required to return (at least) three outputs. There's no way to tell MATLAB that it should leave b and c alone if fname only returns one output.