Azzera filtri
Azzera filtri

How can I make a mock method return different answers?

11 visualizzazioni (ultimi 30 giorni)
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 Commento
Markus Leuthold
Markus Leuthold il 8 Ago 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

Accedi per commentare.

Risposta accettata

David Hruska
David Hruska il 18 Set 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.
  1 Commento
Ian Craig
Ian Craig il 18 Set 2017
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.

Accedi per commentare.

Più risposte (1)

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

Categorie

Scopri di più su Mock Dependencies in Tests in Help Center e File Exchange

Prodotti

Community Treasure Hunt

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

Start Hunting!

Translated by