python mocking: wrong number of arguments error - python

I am trying to use the mock library and basically patching some module functions. So, I have some existing code which looks like this:
#patch('loader.utils.run_raise_exception_if_fail')
#patch('time.time', return_value=123)
def test_export_ok(self, _, run_command_mock):
....
calls = run_command_mock.call_args_list
This sort of works fine and the test gets called and executed. I am trying to understand what these function arguments mean and where they are generated. I have not used the mocking functionality ever before.
Now, I am trying to mock another function and I added the following patch decorator:
#patch('assessment.utils.statistics', return_value={"counts": {'volume': 10, hits=10}})
Now, when I try and run this thing, I get the following error:
TypeError: test_export_ok() takes 2 positional arguments but 4 were given
I am confused as to the patch decorators and these function arguments to the actual test. The function signature for the statistics method looks as follows:
def statistics(collisions: np.ndarray,
obj_size: Union[List, Tuple]):

#patch('loader.utils.run_raise_exception_if_fail')
#patch('time.time', return_value=123)
def test_export_ok(self, a, b):
....
calls = run_command_mock.call_args_list
For whichever function you are writing the unit test, if there are some 2 inner functions getting called in the main function and u want to patch it. Add the 2 patch decorators with the corresponding return value. Add variables in the main test function "a,b" corresponding to the count of the patch decorators used.
Please try this.
The error for incorrect number of arguments given should be resolved.

Related

how to verify all but selected functions from the same module were not called?

I have a simple module (no classes, just utility functions) where a function foo() calls a number of functions from the same module, like this:
def get_config(args):
...
return config_dictionary
def get_objs(args):
...
return list_of_objects
def foo(no_run=False):
config = get_config(...)
if no_run:
return XYZ
objs = get_objects(config)
for obj in objs:
obj.work()
... # number of other functions from the same module called
Is it possible to use Python Mockito to verify that get_config() was the last function called from my module in foo() ? (for certain arguments)
Currently this is verified in this way:
spy2(mymodule.get_config)
spy2(mymodule.get_objects)
assert foo(no_run=True) == XYZ
verify(mymodule).get_config(...)
# Assumes that get_objects() is the first function to be called
# in foo() after the configuration is retrieved.
verify(mymodule, times=0).get_objects(...)
Perhaps something like generating the spy() and verify() calls dynamically ? Rewrite the module into a class and stub the whole class ?
Basically, I do not like the assumption of the test - the code in foo() can be reordered and the test would still pass.
That's not your real code, and then it is often not describing the real problem you have here. If for example you don't expect a function is called at all, like get_objects in your case, then why begin with spy2 in the first place. expect(<module>, times=0).<fn>(...) reads better in that case, and a subsequent verify is not needed.
There is verifyNoMoreInteractions(<module>) and inorder.verify testing. But all this is guessing as you don't tell how XYZ is computed. (Basically why spy2(get_config) and not a when call here. T.i. why calling the original implementation and not mocking the answer?)

Python inspect: Get arguments of specific decorator

I need a script that given a function returns the arguments of a specific decorator.
Imagine the following function:
#decorator_a
#decorator_b(41,42,43)
#decorator_c(45)
def foo(self):
return 'bar'
I need a function that given foo returns the arguments of decorator_b - something like [41,42,43]. Is there a way to achieve this?
After a few hours of trying out different stuff I figured out a feasible solution:
inspect.getclosurevars(foo.__wrapped__).nonlocals
If you know the argument names of the decorator you try to inspect you can check for existence in the nonlocals dict. If it's not there, check one __wrapped__ layer higher and so on.

Mocking a function that is passed in as a parameter during a class variable initialization

scuevals_api/resources/students.py:
def year_in_range(year):
return datetime.now().year <= year <= datetime.now().year + 10
class StudentsResource(Resource):
args = {
'graduation_year': fields.Int(required=True, validate=year_in_range),
}
...
I'm trying to mock year_in_range (to always return True) however, all my attempts have failed so far.
I'm using the decorator approach with mock.patch and have tried a ton of different targets, but the one I believe should be the correct one is:
#mock.patch('scuevals_api.resources.students.year_in_range', return_value=True)
The mock function never gets called, as in, it's not mocking correctly. I'm not getting any errors either.
My only remaining suspicions is that it has something to do with that the function is passed in to fields.Int as a param (hence the question title), but in my head, it shouldn't affect anything.
I'm clueless as to where this function should be mocked?
By the time mock has patched year_in_range it is too late. mock.patch imports the module specified by the string you provided and patches the name indicated within the module so it refers to a mock object - it does not fundamentally alter the function object itself. On import of scuevals_api.resources.students the body of the StudentsResource class will be executed and a reference to the original year_in_range saved within the StudentResource.args['graduation_year'] object, as a result making the name year_in_range refer to a mock object has no impact.
In this particular case you have a few options:
Assuming you're trying to test some functionality, instead of trying to mock year_in_range you can seed the database (?) with data that tests the condition
You can patch datetime.now which will be called by year_in_range
You can patch the member of StudentResource.args['graduation_year'] where the function passed to validate has been saved.
Thanks to the explanation by Chris Hunt, I came up with an alternative solution. It does modify the application code rather than the testing code, but if that is acceptable (which, in today's day and age probably should be, since having testable code is high priority), it is a really simple solution:
It's not possible to mock year_in_range since a reference to the original function is saved before the mocking is done. Therefore, "wrap" the function you want to mock with another function and pass the wrapper instead. Wrapping can be done in a nice and tidy way using lambda functions:
def year_in_range(year):
return datetime.now().year <= year <= datetime.now().year + 10
class StudentsResource(Resource):
args = {
'graduation_year': fields.Int(required=True, validate=lambda y: year_in_range(y)),
}
...
Now, when I mock year_in_range as stated in the question, it will work. The reason is because now a reference is saved to the lambda function, instead of to the original year_in_range (that won't be accessed until the lambda function runs, which will be during the test).

How to mock function with arguments spanning lines

I'm patching in my test (python2.7):
args[1].return_value.getMarkToMarketReportWithSummary.return_value = ([], {})
and I can see the
the expected mocked method with the correct return value when debugging:
and calling it is all good:
But, the method has multiple arguments:
rows, summary = manager.getMarkToMarketReportWithSummary(
portfolios, report_data_map, account,
...
include_twrr=self.__include_twrr)
and when the test runner calls the method it fails and returns a MagicMock instead of expected above. It's because of the arguments, making the method call a string or something. It looks like this:
so the method name looks the same but it has the \n with the args, etc. What is this? Is it an onion? Because it is making me cry.
Evaluating it a after that gives one more attribute, this time with #LINE#, because, you know, rubbing salt in my eyes is its goal:
:_(

How does this function call work? (Involves multiple pairs of brackets and decorators)

On this page of the official Python documentation about decorators and 'compound statements', this sample of code is given:
[Begin of sample]
#f1(arg)
#f2
def func(): pass
is equivalent to:
def func(): pass
func = f1(arg)(f2(func))
[End of sample]
However, I don't understand 'func = f1(arg)(f2(func))'. I've never seen a call like this before, and I have no idea of what it means. Is it multiple calls using different arguments, each pair of brackets containing one argument ('arg' in the first, 'f2(func)' in the second), or is it something else? I need to understand this in order to be able to study decorators. Also, does this work in Python 2.7? One of the sites I consulted on decorators was about Python 3.2.
What you need to know for this is that functions are first class objects - you can pass functions around just like ints or strs.
f1 returns a function, so what you've posted is similar to:
def func(): pass
f1_ret = f1(arg)
f2_ret = f2(func)
func = f1_ret(f2_ret)
The result of a Python function call is a value like any other. If you want, you can write func = f1(arg)(f2(func)) as
def func(): pass
x = f2(func)
y = f1(arg)
func = y(x)
A decorator that takes arguments must return a function that does the actual decorating. So #f1(arg) calls f1(arg). f1 returns a function, which can be called using the usual (...) notation: f1(arg)(...). What goes in the second set of parentheses? The function being decorated, which is f2(func) because of the second decorator. So put it all together and you get f1(arg)(f2(func)).
The piece you're missing, then, is that f1 returns a function and the second set of parentheses are calling the returned function.

Categories