How can I make a mock method return different answers?

2 次查看(过去 30 天)
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 个评论
Markus Leuthold
Markus Leuthold 2023-8-8
"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

请先登录,再进行评论。

采纳的回答

David Hruska
David Hruska 2017-9-18
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.
  1 个评论
Ian Craig
Ian Craig 2017-9-18
Thanks, that's perfect! My test already has a property that defines the number of frames that the mock has to deliver, so I just use that instead of n in your answer.

请先登录,再进行评论。

更多回答(1 个)

Markus Leuthold
Markus Leuthold 2023-8-8
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

类别

Help CenterFile Exchange 中查找有关 Mock Dependencies in Tests 的更多信息

产品

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by