I'm using the mock library written by Michael Foord to help with my testing on a django application.
I'd like to test that I'm setting up my query properly, but I don't think I need to actually hit the database, so I'm trying to mock out the query.
I can mock out the first part of the query just fine, but I am not getting the results I'd like when I chain additional things on.
The function:
#staticmethod
def get_policies(policy_holder, current_user):
if current_user.agency:
return Policy.objects.filter(policy_holder=policy_holder, version__agency=current_user.agency).distinct()
else:
return Policy.objects.filter(policy_holder=policy_holder)
and my test: The first assertion passes, the second one fails.
def should_get_policies_for_agent__user(self):
with mock.patch.object(policy_models.Policy, "objects") as query_mock:
user_mock = mock.Mock()
user_mock.agency = "1234"
policy_models.Policy.get_policies("policy_holder", user_mock)
self.assertEqual(query_mock.method_calls, [("filter", (), {
'policy_holder': "policy_holder",
'version__agency': user_mock.agency,
})])
self.assertTrue(query_mock.distinct.called)
I'm pretty sure the issue is that the initial query_mock is returning a new mock after the .filter() is called, but I don't know how to capture that new mock and make sure .distinct() was called on it.
Is there a better way to be testing what I am trying to get at? I'm trying to make sure that the proper query is being called.
Each mock object holds onto the mock object that it returned when it is called. You can get a hold of it using your mock object's return_value property.
For your example,
self.assertTrue(query_mock.distinct.called)
distinct wasn't called on your mock, it was called on the return value of the filter method of your mock, so you can assert that distinct was called by doing this:
self.assertTrue(query_mock.filter.return_value.distinct.called)
Related
This is my first time building out unit tests, and I'm not quite sure how to proceed here. Here's the function I'd like to test; it's a method in a class that accepts one argument, url, and returns one string, task_id:
def url_request(self, url):
conn = self.endpoint_request()
authorization = conn.authorization
response = requests.get(url, authorization)
return response["task_id"]
The method starts out by calling another method within the same class to obtain a token to connect to an API endpoint. Should I be mocking the output of that call (self.endpoint_request())?
If I do have to mock it, and my test function looks like this, how do I pass a fake token/auth endpoint_request response?
#patch("common.DataGetter.endpoint_request")
def test_url_request(mock_endpoint_request):
mock_endpoint_request.return_value = {"Auth": "123456"}
# How do I pass the fake token/auth to this?
task_id = DataGetter.url_request(url)
The code you have shown is strongly dominated by interactions. Which means that there will most likely be no bugs to find with unit-testing: The potential bugs are on the interaction level: You access conn.authorization - but, is this the proper member? And, does it already have the proper representation in the way you need it further on? Is requests.get the right method for the job? Is the argument order as you expect it? Is the return value as you expect it? Is task_id spelled correctly?
These are (some of) the potential bugs in your code. But, with unit-testing you will not be able to find them: When you replace the depended-on components with some mocks (which you create or configure), your unit-tests will just succeed: Lets assume that you have a misconception about the return value of requests.get, namely that task_id is spelled wrongly and should rather be spelled taskId. If you mock requests.get, you would implement the mock based on your own misconception. That is, your mock would return a map with the (misspelled) key task_id. Then, the unit-test would succeed despite of the bug.
You will only find that bug with integration testing, where you bring your component and depended-on components together. Only then you can test the assumptions made in your component against the reality of the other components.
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).
I'm unit-testing a function with mock, and trying to test if a call to mock object was made. In the code below the requests.post object is mocked, and I track the requests.post.mock_calls list.
In the following code the
Code construct:
import timeout_decorator
def block_funds(trader_id, amounts):
#timeout_decorator.timeout(3, use_signals=False)
def _block_funds(block_url, amounts):
# requests.post.mock_calls empty here
result = requests.post(url=block_url, data=amounts)
# requests.post.mock_calls here has correct call recorded
return result.status_code
block_url = 'http:/someurl/somepath/{trader_id}'.format(trader_id=trader_id)
try:
# requests.post.mock_calls empty here
code = _block_funds(block_url, amounts)
# requests.post.mock_calls empty again here
except timeout_decorator.TimeoutError as ex:
logger.error('request failed')
code = 500
return code
After the call to code = _block_funds(block_url, amounts) I expect the mock object to keep record of all calls to it, but the mock_calls list gets emptied as soon as the execution exits the internal timeout wrapped function _block_funds(). Mock object is certainly the same, I'm following the mock IDs to ensure object has not changed.
What I'm doing wrong and how to make the mock not forget it's calls?
I've found the issue, it's in the timeout decorator, and specifically - in the use_signals=False part of it. As per timeout decorator documentation, to use timeouts correctly in my scenario (multithreaded web application) you need to not use signals and rely on multiprocessing instead, and in this case I see this unexpected mock that causes the problem. If I remove use_signals=False or remove decorator completely - it works fine.
My solution for now will be to mock the decorator itself also and avoid the issue.
Correction
Directly mocking the decorator turned out to be impractical. Instead I've wrapped it around and mocked the wrap:
def timeout_post(**kwargs):
#timeout_decorator.timeout(3, use_signals=False)
def _post(**kwargs):
return requests.post(**kwargs)
return _post(**kwargs)
I have a test below using mock on Python 2.6.
#patch('testme.check.RE.query')
def test_check(query):
query.filter.one = MagicMock(return_value=None)
....
assert against_experiment(cl, ei, cd)
Inside the module I want to test, I have
def check_against_experiment(c, e, c):
re = RE.query.filter(RE.e_id == e).one()
Two questions:
When inspecting re = RE.query.filter(RE.e_id == e).one(), I see <MagicMock name='query.filter().one()' id='62856528'>.
Why isn't this returning None?
Another question is when I change #path('testme.check.RE.query') to #path('testme.check.RE.query') and change query.filter.one to query.one, I notice that nothing is mocked out. Why is this the case?
When you are calling RE.query.filter(RE.e_id == e) in your function-under-test, it is returning a new unconfigured MagicMock, and then you are calling its one function, which is generating that default return_value.
You probably want to set the return_value of filter to be a new Mock whose one function returns None.
Short answer is what you want to do is
query.filter.return_value.one.return_value = None
Default mock's return_value attribute is a MagickMock object (AFAIK allocated as lazy property).
IMHO try to avoid this kind of mock configurations, take your class/function more clear and narrow to avoid object browsing. These kind of test make source and tests tangled and create a solid fence to do refactoring.
For your second question I can just point you to Where to Patch must read document. From what you say is impossible to find the root cause by I can bet the answer is always in this documentation's chapter.
For unit testing, I want to mock a variable inside a function, such as:
def function_to_test(self):
foo = get_complex_data_structure() # Do not test this
do_work(foo) # Test this
I my unit test, I don't want to be dependent on what get_complex_data_structure() would return, and therefore want to set the value of foo manually.
How do I accomplish this? Is this the place for #patch.object?
Just use #patch() to mock out get_complex_data_structure():
#patch('module_under_test.get_complex_data_structure')
def test_function_to_test(self, mocked_function):
foo_mock = mocked_function.return_value
When the test function then calls get_complex_data_structure() a mock object is returned and stored in the local name foo; the very same object that mocked_function.return_value references in the above test; you can use that value to test if do_work() got passed the right object, for example.
Assuming that get_complex_data_structure is a function1, you can just patch it using any of the various mock.patch utilities:
with mock.patch.object(the_module, 'get_complex_data_structure', return_value=something)
val = function_to_test()
...
they can be used as decorators or context managers or explicitly started and stopped using the start and stop methods.2
1If it's not a function, you can always factor that code out into a simple utility function which returns the complex data-structure
2There are a million ways to use mocks -- It pays to read the docs to figure out all the ways that you can set the return value, etc.