Mocking an instance method call in a function - python

I'm currently having my first real experience writing tests by creating a unittest suite in python, and I've run into a problem with my mock framework.
Currently, I have a function that has a line like this:
def data_function():
*Some code*
with AccountDBConnection(account_id) as adb: # Pulling data
data = adb.get_query_results(query)
*Some more code*
I'm trying to test that the function get_query_results is being called correctly. In my tests, I'm attempting to test it as so:
#patch('package.module_that_defines_data_function.AccountDBConnection')
def test_data_function(self, mock_AccountDBConnection):
*Other assertions*
mock_AccountDBConnection.get_query_results.assert_called_once_with(ANY)
*more assertions*
Unfortunately, whenever I try to run this test, I get an AssertionError stating get_query_results is never called. It most certainly is called, and I can't get it to recognize this. I think it's because the method is being called on an instance of an object that is created within the function, but I don't know how to get the mock to recognize. Does anyone have any suggestions?
Thanks a bunch!

Related

Python mock not applied to object under test

I am attempting to mock an object to perform some testing
test.py
#patch("api.configuration.client.get_configuration")
def test(mock_client_get_configuration):
mock_client_get_configuration.return_value = "123"
result = code_under_test()
assert result
Inside code_under_test() I make the concrete call to get_configuration().
code.py
from api.configuration.client import get_configuration
def code_under_test():
config = get_configuration("a", "b")
However, whenever I run my test, get_configuration is always the concrete version and not my mock.
If I add a print in my test, I can see that mock_client_get_configuration is a MagicMock.
If I add a print inside code_under_test, then get_configuration is always the actual <function.... and not my mock.
I feel somewhere I am going wrong in how I create my mock. Perhaps it is because my mock does not mimic the two parameters needed on the concrete call so the signature is incorrect?
I'm using Python 3.9 and pytest 7.0.1.

Python testing: mocking a function that's imported AND used inside another funciton

Due to circular-import issues which are common with Celery tasks in Django, I'm often importing Celery tasks inside of my methods, like so:
# some code omitted for brevity
# accounts/models.py
def refresh_library(self, queue_type="regular"):
from core.tasks import refresh_user_library
refresh_user_library.apply_async(
kwargs={"user_id": self.user.id}, queue=queue_type
)
return 0
In my pytest test for refresh_library, I'd only like to test that refresh_user_library (the Celery task) is called with the correct args and kwargs. But this isn't working:
# tests/test_accounts_models.py
#mock.patch("accounts.models.UserProfile.refresh_library.refresh_user_library")
def test_refresh_library():
Error is about refresh_library not having an attribute refresh_user_library.
I suspect this is due to the fact that the task(refresh_user_library) is imported inside the function itself, but I'm not too experienced with mocking so this might be completely wrong.
Even though apply_async is your own-created function in your core.tasks, if you do not want to test it but only make sure you are giving correct arguments, you need to mock it. In your question you're mocking wrong package. You should do:
# tests/test_accounts_models.py
#mock.patch("core.tasks.rehresh_user_library.apply_sync")
def test_refresh_library():
In your task function, refresh_user_library is a local name, not an attribute of the task. What you want is the real qualified name of the function you want to mock:
#mock.patch("core.tasks.refresh_user_library")
def test_refresh_library():
# you test here

Django mocks not working as expected

I'm struggling with django mock; I have even simplified an unit test but the test is still failing. I want to verify that a method is called (even with any parameter), but the "assert_called_once_with" always returns False.
Currently I'm trying:
#patch('utils.make_reset_password')
def test_shouldHaveCalledMakeResetToken(self, mocked):
user = User.get(...)
make_reset_password(user)
mocked.assert_called_once_with(user)
Even this simple example is failing with:
AssertionError: Expected 'make_reset_password' to be called once. Called 0 times
How this is possible? What am I doing wrong?
Thanks in advance
You have to use full path to utils, e.g. #patch('my_app.utils.make_reset_password') and then in the test call a function that calls make_reset_password.
#patch('my_app.utils.make_reset_password')
def test_shouldHaveCalledMakeResetToken(self, mock_make_reset_password):
user = User.get(...)
function_under_test(user)
mock_make_reset_password.assert_called_once_with(user)
EDIT
The other thing that comes to my mind is you are not mocking the correct function. If make_reset_password is imported from utils in another module then you need to change the path in the #patch decorator.
For example
# my_module.py
from my_app.utils import make_reset_password
def run_make_reset_password(user):
make_reset_password(user)
# tests.py
#patch('my_app.my_module.make_reset_password')
def test_shouldHaveCalledMakeResetToken(self, mock_make_reset_password):
user = User.get(...)
run_make_reset_password(user)
mock_make_reset_password.assert_called_once_with(user)

Python: know function is called from a unittest?

Is there a way to know in Python if a function is called from the context of a unittest execution or a debugging run?
For the context, I am trying to unittest a code where I use functions that perform a database call. In order to avoid database calls during the test of that function (DB calls are tested separately), I am trying to make the DB IO functions aware of their environement and to mock when they are called within a unittest and log additional variables during a debug run.
My current aproach is to read/write environment variables, but it seems a little bit of an overkill and I think Python must have a better mechanism for that.
Edit:
Here is the example of a function I am trying to unittest:
from Database_IO import Database_read
def some_function(significance_level, time_range)
data = Database_read(time_range)
significant_data = data > significance_level
return significant_data
In my opinion, if you write your function to behave in a different way when tested, you are not really testing it.
To test the function I'd mock.patch() the database object, and then check it has used correctly in your function.
The most difficult thing when you start using the mock library is to find the correct object to replace.
In your example, if in your_module you import the Database_read object from the Database_IO module, you can test it by using a code similar to the following
with mock.patch('your_module.Database_read') as dbread_mock:
# prepare the dbread_mock
dbread_mock.return_value = 10
# execute a test call
retval = some_function(3, 'some range')
# check the result
dbread_mock.assert_called_with('some range')

Django Test Case Can't run method

I am just getting started with Django, so this may be something stupid, but I am not even sure what to google at this point.
I have a method that looks like this:
def get_user(self,user):
return Utilities.get_userprofile(user)
The method looks like this:
#staticmethod
def get_userprofile(user):
return UserProfile.objects.filter(user_auth__username=user)[0]
When I include this in the view, everything is fine. When I write a test case to use any of the methods inside the Utility class, I get None back:
Two test cases:
def test_stack_overflow(self):
a = ObjName()
print(a.get_user('admin'))
def test_Utility(self):
print(Utilities.get_user('admin'))
Results:
Creating test database for alias 'default'...
None
..None
.
----------------------------------------------------------------------
Can someone tell me why this is working in the view, but not working inside of the test case and does not generate any error messages?
Thanks
Verify whether your unit test comply the followings,
TestClass must be written in a file name test*.py
TestClass must have been subclassed from unittest.TestCase
TestClass should have a setUp function to create objects(usually done in this way, but objects creation can happen in the test functions as well) in the database
TestClass functions should start with test so as to be identified and run by the ./manage.py test command
TestClass may have tearDown to properly end the unit test case.
Test Case Execution process:
When you run ./manage.py test django sets up a test_your_database_name and creates all the objects mentioned in the setUp function(Usually) and starts executing the test functions in the order of placement inside the class and once when all the test functions are executed, finally looks for the tearDown function executes if any present in the test class and destroys the test database.
It may be because that you might not have invoked objects creation in setUp function or elsewhere in the TestClass.
Can you kindly post the entire traceback and test file to help you better?

Categories