How can I make a mock method return different answers?
11 visualizaciones (últimos 30 días)
Mostrar comentarios más antiguos
Ian Craig
el 18 de Sept. de 2017
Respondida: Markus Leuthold
el 8 de Ag. de 2023
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 comentario
Markus Leuthold
el 8 de Ag. de 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
Respuesta aceptada
David Hruska
el 18 de Sept. de 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.
Más respuestas (1)
Markus Leuthold
el 8 de Ag. de 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 comentarios
Ver también
Categorías
Más información sobre Mock Dependencies in Tests en Help Center y File Exchange.
Productos
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!