68 views (last 30 days)
Show older comments
Louis on 13 Nov 2020
Commented: Jon on 13 Nov 2020
Open in MATLAB Online
This is my first time writing unit tests, so advice from experienced users will be greatly appreciated.
I have several compute functions from which many of them take in the same input arguments, and I would like to write efficient unit tests with minimum code duplication.
I have a following working test class:
%% Test Class Definition
classdef ComputeFunction1Test < matlab.unittest.TestCase
%% Test Method Block
methods (Test)
% includes unit test functions
%% Test Function
function testFunction1FirstOutput(testCase)
% Actual solution - AB contribution
load('test_data.mat') % variables input1, input2, and input3
[act, ~] = computeFunction1(input1, input2, input3);
% Verify using expected AB contribution
load('function1ExpectedOut1.mat'); % variable exp contains expected output
testCase.verifyEqual(act, exp);
end
function testFunction1SecondOutput(testCase)
% Actual solution - normalization factor for AB
load('test_data.mat')
[~, act] = computeFunction1(input1, input2, input3);
% Verify using expected normalization factor
load('function1ExpectedOut2.mat'); % variable exp contains expected output
testCase.verifyEqual(act, exp);
end
end
end
I have several more different functions that take the same input arguments and produce output in the same format as computeFunction1.m, which above test class tests. (say computeFunction2.m, computeFunction3.m, and computeFunction3.m, ...)
I could copy the above test class and change the function name to test other functions that take the same input arguments, but this seems inefficient.
Is there a way to handle this situation more efficiently?
Note that I do have other compute functions that require different input arguments as well that need to be tested too. (say specialComputeFunction1 and specialComputeFunction2.m)
2 Comments Show NoneHide None
Show NoneHide None
Jon on 13 Nov 2020
Direct link to this comment
https://support.mathworks.com/matlabcentral/answers/646903-how-to-unit-test-several-different-functions-that-take-the-same-input-arguments-efficiently#comment_1131923
Maybe you could do it in a loop. Put all of the expected results in a single matrix, with a column for each argument
then just evalute the function once returning all of the output arguments and have a loop to compare each argument with appropriate column in expected results matrix. Return vector of pass fail logicals with an element for each output argument. In your example, just two elements, but it could be any number
Jon on 13 Nov 2020
Direct link to this comment
https://support.mathworks.com/matlabcentral/answers/646903-how-to-unit-test-several-different-functions-that-take-the-same-input-arguments-efficiently#comment_1131988
⋮
Sorry, I should have looked more deeply into your question. I hadn't realized that you were using a built in unit test class provided by MATLAB. I would then steer you to looking at the examples and documentation for that. I'm sure there is a standard approach for what you are trying to do as it seems like a typical usecase. I will look into this more myself, would probably be helpful in my work to use this built in testing functionality
Sign in to comment.
Sign in to answer this question.
Answers (3)
Sean de Wolski on 13 Nov 2020
Edited: Sean de Wolski on 13 Nov 2020
Open in MATLAB Online
Look into using TestParameters. You can use an array of function handles as the parameter values to traverse different functions and pass the results as parameters as well. You'll need to run them with ParameterCombination=sequential. Here's a functional example:
classdef tFcnParameter < matlab.unittest.TestCase
properties (TestParameter)
fcn = struct('plus',@plus,'minus',@minus)
result = {5 1}
end
methods (Test, ParameterCombination = 'sequential')
function shouldApplyFcn(testCase, fcn, result)
r = fcn(3, 2);
testCase.verifyEqual(r, result);
end
end
end
0 Comments Show -2 older commentsHide -2 older comments
Show -2 older commentsHide -2 older comments
Sign in to comment.
Steven Lord on 13 Nov 2020
Open in MATLAB Online
%% Test Class Definition
classdef ComputeFunction1Test < matlab.unittest.TestCase
%% Test Method Block
methods (Test)
% includes unit test functions
%% Test Function
function testFunction1FirstOutput(testCase)
% Actual solution - AB contribution
load('test_data.mat') % variables input1, input2, and input3
[act, ~] = computeFunction1(input1, input2, input3);
% Verify using expected AB contribution
load('function1ExpectedOut1.mat'); % variable exp contains expected output
testCase.verifyEqual(act, exp);
% *snip rest of test file*
You don't want to do this. The identifier exp already has a meaning in MATLAB, and there's no indication that it should be a variable when MATLAB parses the function. If you must load data in a function I strongly encourage you to call load with an output argument so you don't "poof" variables into the workspace.
Rather than using MAT-files, if the output arguments are "small" consider hard-coding them. Or consider using the definition of the operation that computeFunction1 performs to determine a way to validate the results without hard-coding them. For instance, if I were validating the svd function (accepts A and computes U, S, and V such that U*S*V' effectively equals A) a valid verification could be "is U*S*V' 'close enough' to A?" This avoids having to create hard-coded copies of expected U, S, and V matrices.
[U, S, V] = svd(A);
testCase.verifyEqual(U*S*V', A, 'AbsTol', someTolerance)
You can also have multiple verify* calls in the same method. I've labeled each portion of the test with which of the four phases they implement.
classdef testBounds < matlab.unittest.TestCase
methods(Test)
function simpleTest1(testCase)
% Setup phase
x = 1:10;
% Exercise phase
[minValue, maxValue] = bounds(x);
% Verify phase
testCase.verifyEqual(minValue, 1);
testCase.verifyEqual(maxValue, 11); % Whoops!
% Teardown phase
%
% x, minValue, and maxValue will automatically be destroyed when the function exits
% so there's no need for explicit teardown for this test.
%
% If the test had opened a figure window (for example) this is where you'd want to
% close that figure window. Actually in that case I would have used addTeardown
% right after I created the figure in the Setup phase so it would be closed
% even if the Exercise or Verify phases threw a hard error. But here is where that
% teardown added by addTeardown would trigger under normal execution.
end
end
end
If you run this, the test failure message will indicate the problem is on the line I've commented "Whoops!"
Be wary of trying to test too much in any individual test method, but in this case testing both outputs from a single function call doesn't strike me as too much.
That being said, if you have a collection of inputs and the corresponding outputs you could write a parameterized test. See the testNumel test method on that page for an example.
0 Comments Show -2 older commentsHide -2 older comments
Show -2 older commentsHide -2 older comments
Sign in to comment.
Jon on 13 Nov 2020
Edited: Jon on 13 Nov 2020
Here are a couple of possibilities
You can pass function handles, and then make the function you are testing be one of the arguments to your test program https://www.mathworks.com/help/matlab/matlab_prog/pass-a-function-to-another-function.html
You can also use the MATLAB function eval to evaluate expressions that are written as strings however this seems clumsy, I would prefer using function handles
Also in your example above, why not call the function once, evaluate both output arguments, and then test them rather than calling the function twice, and having all that cut and paste code.
3 Comments Show 1 older commentHide 1 older comment
Show 1 older commentHide 1 older comment
Louis on 13 Nov 2020
Direct link to this comment
https://support.mathworks.com/matlabcentral/answers/646903-how-to-unit-test-several-different-functions-that-take-the-same-input-arguments-efficiently#comment_1131748
Open in MATLAB Online
Thank you - I will take a look at the document you referenced.
First output is a list of double whereas the second output is just a single double value, and I wasn't able to find compelling example in the document to evaluate both argument at once.
Could you provide an example for testing both?
I suppose I could just append the second output to the first output and test them together:
testCase.verifyEqual([act1; act2], [exp1; exp2]);
But wouldn't this make it hard to tell where the mismatch is exactly coming from in case this test fails?
Jon on 13 Nov 2020
Direct link to this comment
https://support.mathworks.com/matlabcentral/answers/646903-how-to-unit-test-several-different-functions-that-take-the-same-input-arguments-efficiently#comment_1132008
Maybe you could do it in a loop. Put all of the expected results in a single matrix, with a column for each argument
then just evalute the function once returning all of the output arguments and have a loop to compare each argument with appropriate column in expected results matrix. Return vector of pass fail logicals with an element for each output argument. In your example, just two elements, but it could be any number
Jon on 13 Nov 2020
Direct link to this comment
https://support.mathworks.com/matlabcentral/answers/646903-how-to-unit-test-several-different-functions-that-take-the-same-input-arguments-efficiently#comment_1132013
Sorry, I should have looked more deeply into your question. I hadn't realized that you were using a built in unit test class provided by MATLAB. I would then steer you to looking at the examples and documentation for that. I'm sure there is a standard approach for what you are trying to do as it seems like a typical usecase. I will look into this more myself, would probably be helpful in my work to use this built in testing functionality
Sign in to comment.
Sign in to answer this question.
See Also
Categories
MATLABLanguage FundamentalsData TypesNumeric TypesLogical
Find more on Logical in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!
An Error Occurred
Unable to complete the action because of changes made to the page. Reload the page to see its updated state.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- Deutsch
- English
- Français
- United Kingdom(English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)
Contact your local office