Pytest Django function mocking APITestCase - python

I am trying to create tests involving sending GET requests to my API using pytest-django and I need a function used in the views to be mocked.
I have tried mocker from pytest-mock and unittest.mock.patch and every time I mock this function in some test case it remains mocked in the other tests as well.
First .py test file:
from unittest.mock import patch
from rest_framework.test import APITestCase
import pytest
#pytest.mark.django_db
class TestFirst(APITestCase):
#classmethod
def setUpClass(cls):
cls.patcher = patch(app.views.function)
cls.patcher.start()
#classmethod
def tearDownClass(cls):
cls.patcher.stop()
def test_something(self):
get_data = self.client.get('/some/url')
self.assertEqual(200, get_data.status_code)
and then followed by a test in some completely different .py file:
from rest_framework.test import APITestCase
import pytest
#pytest.mark.django_db
class TestSecond(APITestCase):
def test_something_else(self):
get_data = self.client.get('/some/url')
self.assertEqual(200, get_data.status_code)
When debugging the first test case, the method is patched correctly. However when running the second test, the method remains patched and the mock object keeps the number of calls received.
Am I missing something important?
EDIT: I tried both patching the file where the method is defined and name of the method in views, but always keep getting same result.
EDIT2: Worth noting that when I change the order of the tests, the second one completes correctly, but the first one is unable to have the method patched and calls it unpatched, therefore fails.

I resolved the issue by using the SimpleTestCase superclass. I have still no idea why this was happenning, but doesn't seem to be anymore.

Related

pytest can't mock a method when its called by another module

I can't seem to mock a method of one of my modules. I can mock it inside the unit test itself, but not in another method that calls it.
# Inside a module
from my_module import my_method
def some_method_that_calls_my_module_my_method(arg):
my_method(arg)
# In my unit test
#patch("my_module.my_method", mock.MagicMock(return_value="test1"))
def test_test():
# this works
print(my_module.my_method("slkdjflskdjfl"))
# this executes the method not the mock
some_method_that_calls_my_module_my_method("sldkfjlskdjflskdjf")
I'll add I really want to use the pytest decorator here if I can- I much prefer it to with.

How to mock external module calls and test code in the class body

I want to store static information in a class, shared by all the instances. The information is something obtained by using another module, I only want to do this once, eg. let's say that this is what I have in mymodule.py:
import os
MyClass:
bus = os.environ.get('DBUS_SESSION_BUS_ADDRESS', None)
def __init__(self):
pass
How can I test this code and mock os.environ.get to make sure that the call is made correctly?
Since the execution happens at the time of the first import, I would need to reload the module in the test, but even so, I can't have os.environ.get mocked at the right time:
import unittest
from unittest import patch, MagicMock
import importlib
import mymodule
class TestMyClass(unittest.TestCase):
#patch('mymodule.os.environ', spec=['get'])
def test_class_init_patch(self, mock_env):
# Too early - the reload overrides the mock
importlib.reload(mymodule)
mock_env.get.assert_called_once_with('DBUS_SESSION_BUS_ADDRESS', None)
def test_class_init_mock(self):
importlib.reload(mymodule)
# Too late - the class body already executed
mymodule.MyClass.os.environ = MagickMock()
I have managed to come up with two alternatives, that make this testable:
Move the class initialization code into a class method and call it from the class body. I can test this class method in my unit test.
Move the class initialization into the __init__ method, guarded by a flag stored in a class variable so that it is only initialized once on the first instantiation.
While both of these should work just fine, it just feels more clean and straightforward to leave this in the class body if possible.
I have managed to figure out how to do this the right way (I hope)!
If you have imported only the module and not the class, function into your code's namespace, eg.:
like this: import os
and not like this: from os import geteuid
You can do this in your test to patch the os module directly:
import os
import unittest
from unittest import patch
import importlib
import mymodule
class TestMyClass(unittest.TestCase):
#patch('os.environ.get')
def test_class_init_patch(self, mock_env_get):
importlib.reload(mymodule)
mock_env_get.assert_called_once_with('DBUS_SESSION_BUS_ADDRESS', None)
This is described in the official documentation as well:
However, consider the alternative scenario where instead of from a import SomeClass module b does import a and some_function uses a.SomeClass. Both of these import forms are common. In this case the class we want to patch is being looked up in the module and so we have to patch a.SomeClass instead:
More details in the official documentation.
This way the module is being patched directly and reloading your own module doesn't affect the patching.
Once the test has run, the patching of the module is undone as usual, however, keep in mind, that the state of your class remains the same way as it was initialized while the external module was patched, so you might need to reload the module again before you run your other tests.
The easiest way to make sure, that you reclaim your "normal" class state, is to reload the module in your TestCase object's setUp method, eg.:
def setUp(self):
importlib.reload(mymodule)

Why is mock.patch class decorator not working?

I want to mock something for an entire class but the following minimal example is not working:
import time
import six
if six.PY2:
import mock
else:
from unittest import mock
#mock.patch('time.sleep', mock.Mock(side_effect=Exception('dooooom')))
class Foo(object):
def bar(self):
print('before')
time.sleep(1)
print('after')
f = Foo()
f.bar()
I get this unexpected output: (why did time.sleep not raise?)
before
after
However if I move the #mock.patch(...) down 1 line so it is decorating the method bar instead of the class Foo then it works as expected:
before
...
Exception: blah
Why does #mock.patch not work at the class level?
It turns out the class decorator only patches methods beginning with patch.TEST_PREFIX which defaults to test.
So renaming the method to test_bar or even testbar makes the patch start working.
Docs:
Patch can be used as a TestCase class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. patch() finds tests by looking for method names that start with patch.TEST_PREFIX. By default this is 'test', which matches the way unittest finds tests. You can specify an alternative prefix by setting patch.TEST_PREFIX.
Evidently this behaviour applies to any class whether inheriting from unittest.TestCase or not.

mocking Flask before_first_request

I have a Flask app and it has a before_first_request method defined. The method loads some cached data for the application. I am trying to run some unit tests, and the cached data is in the way. How can I mock the method.
#app.before_first_request
def load_caches():
print "loading caches..."
# cache loading here.
in my test file, I define a global test_client as follows:
from unittest import TestCase
from .. import application
import mock
test_app = application.app.test_client()
My test classes follow that. The issue is that my test_app loads the cache and I need to mock that in my tests.
You can manually remove hooks in your test client:
test_app = application.app.test_client()
test_app.before_first_request_funcs = []
I'm surprised nobody gave a solution to this. I figure it may be useful to someone else at least. This may be a workaround, though I found this the easiest. In my case, I was testing a standalone function, not a method.
I was banging my head on your question too. I found that installing the python undecorated library and importing it in the file executing unit tests did the trick. Then after doing that, calling the undecorated method call inside of the SetUp method (before running the test_client())Something like this:
In test_my_module.py
from my_app import app, my_module
from undecorated import undecorated
class MyTestClass(unittest.TestCase):
def setUp(self):
undecorated(my_module.my_function)
# we are doing this before anything else due to the decorator's nature
# my_function has the #before_first_request decorator.
# Other setUp code below
self.client = app.test_client()
# ...
I did not find a way to mock the function directly, but i can mock functions called within it:
#app.before_first_request
def before_first_request():
load_caches()
def load_caches():
print "loading caches..."

Why do I need to call my mock like a method?

I've read all I can find with regards to Python 3 and mocking. Unfortunately, I still can't figure out why I need to verify assertions on mock() instead of mock when all documentation I've read use the latter.
Here is what I test looks like -
from unittest.mock import Mock, patch
from unittest import TestCase, skip
from suds.client import Client, ServiceDefinition
from run import WebService
import unittest
#patch('run.Client')
def test(self, mock):
service = WebService()
weather_id = 1234
weather = service.get_weather(weather_id)
mock().service.GetWeather.assert_called_once_with(weather_id)
run in this case is where WebService resides and Client is the suds client.
Printing mock.mock_calls I see -
[
call('SERVICE_WSDL', proxy={'https': 'PROXY', 'http': 'PROXY'}),
call().factory.create('AuthHeader'),
call().set_options(soapheaders=<MagicMock name='Client().factory.create()' id='37553264'>),
call().service.GetWeather(1234, '', '')
]
Mind you, my test passes. I'm simply wondering what I'm missing so I can better understand mocking in Python.
First of all, let's rename the variable because it's actually a mocked instance of run.Client:
#patch('run.Client')
def test(self, mock_client):
# ...
mock_client().service.GetWeather.assert_called_once_with(weather_id)
You're creating an instance of Client within run and you're using that in your code. But you're not actually mocking that instance in this test, you're mocking the class (run.Client is being patched).
So we have a mocked class and we're calling that class to create an instance. That instance is what your code actually uses. This means what you really want is access to the return value of the constructor of the class:
mock_client.return_value.service.GetWeather.assert_called_once_with(weather_id)
This is the same as your code except without calling mock_client(). Your current test code does something similar: call the mocked class, see what instance you get back and perform assertions on that. Yet the mock library provides the return_value attribute to do that already.

Categories

Resources