How can I make a mock method return different answers?
11 views (last 30 days)
Show older comments
I am trying to use the mocking framework to test a class method that computes a running variance of a stream of image frames. The input to the method would normally come from another data class. What I would like to do is to have a mock that returns a normally distributed random frame each time it gets called, and then have a test that checks to see if, after some large number of frames, the returned variance is 1 (within a tolerance).
If I write my method like this:
function mock = makeMock2(obj)
[mock, b] = createMock(obj, 'AddedMethods', {'r'});
import matlab.mock.actions.AssignOutputs;
rng(1)
x = @() randn(obj.rows, obj.cols);
when(withAnyInputs(b.r), repeat(32, AssignOutputs(x())));
end
Then when the output is assigned, randn is evaluated and the output is static, so all my inputs are the same. The second thing I tried was to add multiple frames all at once as a list:
function mock = makeMock3(obj)
[mock, b] = createMock(obj, 'AddedMethods', {'r'});
import matlab.mock.actions.AssignOutputs;
rng(1)
x = num2cell(randn(obj.rows, obj.cols, 32), [1 2]);
when(withAnyInputs(b.r), AssignOutputs(x{:}));
end
This still gives only the first frame because AssignOutputs deals its values to the output variables, and so I'm always only requesting the first one.
The last ugly thing I tried was:
when(withAnyInputs(b.r), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
then(AssignOutputs(randn(obj.rows, obj.cols)), ...
AssignOutputs(randn(obj.rows, obj.cols))) ...
)))))))))))))))))))))))))))))));
This gives me an error that says that I am nested too deep.
I will note that the methods I'm trying to test want the whole mock object as an input, not just the frame, so just passing in a random frame isn't possible. It's part of a much, much larger code base and so simply rewriting the method under test to take the frame directly is not currently possible without significantly refactoring many others as well, so I'm stuck doing the test this way for now.
Is there an elegant way to do what I want to do?
1 Comment
Markus Leuthold
on 8 Aug 2023
"Invoke" was implemented in 2018b, could be your question triggered this enhancement :)
testCase = matlab.mock.TestCase.forInteractiveUse;
[mock,behavior] = testCase.createMock("AddedMethods","roll");
import matlab.mock.actions.Invoke
when(withExactInputs(behavior.roll),Invoke(@(~)randi(6)))
val1 = mock.roll
val2 = mock.roll
Accepted Answer
David Hruska
on 18 Sep 2017
Unfortunately, there isn't a clean way to do this. I'll record this question in an enhancement request, though, for consideration for a future release.
Building on the last workaround you tried: the action can be built up in a loop. This avoids most of the duplication and resulting deep nesting. Here's an example:
import matlab.mock.actions.AssignOutputs;
[mock, b] = createMock(obj, 'AddedMethods', {'r'});
rng(1)
n = 32;
action = AssignOutputs(randn(obj.rows, obj.cols));
for i = 1:n-1
action = action.then(AssignOutputs(randn(obj.rows, obj.cols)));
end
when(withAnyInputs(b.r), action);
Then, when you use the mock (I specified rows = cols = 1 for brevity):
for i = 1:n
disp(mock.r);
end
It returns the sequence of random numbers as desired:
-0.6490
1.1812
-0.7585
-1.1096
-0.8456
-0.5727
-0.5587
0.1784
-0.1969
0.5864
-0.8519
0.8003
-1.5094
0.8759
-0.2428
0.1668
-1.9654
-1.2701
1.1752
2.0292
-0.2752
0.6037
1.7813
1.7737
-1.8651
-1.0511
-0.4174
1.4022
-1.3677
-0.2925
1.2708
0.0660
You just have to select "n" large enough when setting up the mock as any further calls to the method will return this last value.
More Answers (1)
Markus Leuthold
on 8 Aug 2023
Example from Invoke function handle when method is called - MATLAB - MathWorks Switzerland (implemented in R2018b)
testCase = matlab.mock.TestCase.forInteractiveUse;
[mock,behavior] = testCase.createMock("AddedMethods","roll");
import matlab.mock.actions.Invoke
when(withExactInputs(behavior.roll),Invoke(@(~)randi(6)))
val1 = mock.roll
val2 = mock.roll
0 Comments
See Also
Categories
Find more on Mock Dependencies in Tests in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!