Mock openshift-restclient-python resource - python

I'm trying to write some unittest for functions that uses openshift python client so I need to mock some openshift client calls.
https://github.com/openshift/openshift-restclient-python
from kubernetes import client, config
from openshift.dynamic import DynamicClient, exceptions as openshift_exceptions
_openshift_cert_manager_api_version = 'cert-manager.io/v1'
class OCPException(Exception):
pass
def certificate_exists(dyn_client: DynamicClient, namespace: str, certificate_name: str) -> bool:
""" Checks if certificate already exists in namespace. """
v1_certificates = dyn_client.resources.get(api_version=_openshift_cert_manager_api_version, kind='Certificate')
try:
v1_certificates.get(namespace=namespace, name=certificate_name)
except openshift_exceptions.NotFoundError:
return False
except openshift_exceptions as Error:
raise OCPException(Error)
return True
import unittest
from unittest import mock
from openshift.dynamic import DynamicClient
from awscertmanager import ocp
class TestCertificateExists(unittest.TestCase):
def test_certificate_exists(self):
mock_client = mock.create_autospec(DynamicClient)
ocp.certificate_exists(mock_client, namespace='dummy', certificate_name='tls-wildcard-dummy')
mock_client.resources.get.assert_called_with(
api_version=ocp._openshift_cert_manager_api_version,
kind='Certificate'
)
if __name__ == '__main__':
unittest.main()
This test works fine with the first call but I've tried to know if the second call (v1_certificates.get) is called with no success.
I've tried mocking Resource class but I get this error.
import unittest
from unittest import mock
from openshift.dynamic import DynamicClient, Resource
from awscertmanager import ocp
class TestCertificateExists(unittest.TestCase):
#mock.patch.object(Resource, 'get')
def test_certificate_exists(self, mock_get):
mock_client = mock.create_autospec(DynamicClient)
ocp.certificate_exists(mock_client, namespace='dummy', certificate_name='tls-wildcard-dummy')
mock_client.resources.get.assert_called_with(
api_version=ocp._openshift_cert_manager_api_version,
kind='Certificate'
)
mock_get.assert_called_with(namespace='dummy', name='tls-wildcard-dummy')
if __name__ == '__main__':
unittest.main()
Error
Traceback (most recent call last):
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/case.py", line 60, in testPartExecutor
yield
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/case.py", line 676, in run
self._callTestMethod(testMethod)
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/case.py", line 633, in _callTestMethod
method()
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1322, in patched
with self.decoration_helper(patched,
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/contextlib.py", line 113, in __enter__
return next(self.gen)
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1304, in decoration_helper
arg = exit_stack.enter_context(patching)
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1393, in __enter__
original, local = self.get_original()
File "/usr/local/Cellar/python#3.8/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1366, in get_original
raise AttributeError(
AttributeError: <class 'openshift.dynamic.resource.Resource'> does not have the attribute 'get'

Finally it's working mocking DynamicClient class and some calls:
#mock.patch('awscertmanager.ocp.DynamicClient')
def test_certificate_exists_true(self, mock_dynamicclient):
mock_resource = mock.Mock()
mock_resource.get.return_value = None
mock_dynamicclient.resources.get.return_value = mock_resource
result = ocp.certificate_exists(mock_dynamicclient, namespace=self.namespace, certificate_name=self.certificate_name)
mock_resource.get.assert_called_with(namespace=self.namespace, name=self.certificate_name)
self.assertEqual(result, True)

Related

Django Rest: how to mock FileField and ImageField to_representation()

I have a model with 2 fields: image (ImageField) and media (FileField). They both use a custom GoogleCloudMediaStorage.
When writing my tests, I cannot access GCS, so I mocked the writing of the files by doing this:
from unittest.mock import patch
#patch('my_project.storage_backends.CustomGoogleCloudMediaStorage.save')
def test(self, mock_save):
mock_save.return_value = 'mocked_file.png'
# rest of the test
And it's working fine. The problem is now I have to test update and list views. When I do so, I have of course this error:
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application.
After digging it a bit, I see that the error is triggered by the serializer to_representation(). And more precisely in the FileField:
def to_representation(self, value):
if not value:
return None
use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
if use_url:
try:
url = value.url # !! THIS LINE CAUSE THE ISSUE
except AttributeError:
return None
request = self.context.get('request', None)
if request is not None:
return request.build_absolute_uri(url)
return url
So I tried to mock FileField.to_representation() doing this:
#patch('django.db.models.FileField.to_representation')
def test_update(self, mock_representation):
mock_representation.return_value = 'mocked_training_file_url'
data = {
'title': 'Updated',
}
response = self.client.patch(. . .)
But I have this error:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/unittest/mock.py", line 1322, in patched
with self.decoration_helper(patched,
File "/usr/local/lib/python3.8/contextlib.py", line 113, in __enter__
return next(self.gen)
File "/usr/local/lib/python3.8/unittest/mock.py", line 1304, in decoration_helper
arg = exit_stack.enter_context(patching)
File "/usr/local/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/usr/local/lib/python3.8/unittest/mock.py", line 1393, in __enter__
original, local = self.get_original()
File "/usr/local/lib/python3.8/unittest/mock.py", line 1366, in get_original
raise AttributeError(
AttributeError: <class 'django.db.models.fields.files.FileField'> does not have the attribute 'to_representation'
Any idea how I can mock FileField and ImageField to_representation() ? Am I doing this correctly

Python - How to expose a prometheus server from a subprocess?

I have a service which initiates 3 sub-process from main. I need a prometheus metrics server in one of the process. But I get "AttributeError: Can't pickle local object 'MultiProcessValue..MmapedValue'" when the process with prometheus server is started.
main.py:
from os.path import dirname, join
environ['PROMETHEUS_MULTIPROC_DIR'] = join(dirname(__file__), 'prom')
import multiprocessing
from sub_process import SubProcessClass
environ['PROMETHEUS_MULTIPROC_DIR'] = join(dirname(__file__), 'prom')
if __name__ == "__main__":
...
multiprocessing.Process(target=SubProcessClass().f).start()
sub_process.py:
from my_metrics import MyMetric
class SubProcessClass:
def __init__(self):
self.metrics = MyMetric(8090)
self.metrics.start()
def f(self):
print("In f")
self.metrics.inc_my_counter()
my_metrics.py:
from prometheus_client import Counter, CollectorRegistry, multiprocess
from prometheus_client import start_http_server
class MyMetric:
def __init__(self, port):
self.port = port
self.registry = CollectorRegistry()
multiprocess.MultiProcessCollector(self.registry)
self.my_counter = Counter('counter_name', 'counter_desc', registry=self.registry)
def start(self):
start_http_server(self.port)
def inc_my_counter(self):
self.my_counter.inc()
Getting below exception on running main.py
Traceback (most recent call last):
File "<project_path>\source\main.py", line 15, in <module>
multiprocessing.Process(target=MyClass().f).start()
File "<python_path>\lib\multiprocessing\process.py", line 121, in start
self._popen = self._Popen(self)
File "<python_path>\lib\multiprocessing\context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "<python_path>\lib\multiprocessing\context.py", line 327, in _Popen
return Popen(process_obj)
File "<python_path>\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
reduction.dump(process_obj, to_child)
File "<python_path>\lib\multiprocessing\reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object 'MultiProcessValue.<locals>.MmapedValue'
I am running python 3.9.7 on windows.

patching while testing flask app causes AssertionError: View function mapping is overwriting an existing endpoint function

I am trying to write tests for a Flask app.
Currently I have a test that looks like this in a file called test.py.
import mock
from test_helpers import mock_volume_views_setup
class UserTest(unittest.TestCase):
def setUp():
<some set up>
def tearDown():
<some tear down>
#mock.patch('brain_db.views.volume_views_setup')
def test_labeled_scan(self, mock_volume_views_setup):
# test that if path to atlas exists in the container,
self.register_and_commit_user()
self.login_profile_consent()
self.upload('static/test.nii.gz', 1, '04/04/2008', 'GE', '1.5T')
rv = self.app.get('/label_view/1', follow_redirects=True)
response = str(rv.data)
assert "MRI not labeled yet" not in response
test.py lives a top level directory, flask_brain_db.
In flask_brain_db/brain_db/views.py lives the route I'm trying to test. A highly simplified version:
from brain_db_helpers import volume_views_setup
#app.route('/surface_test')
def surface_test():
return render_template('brain_db/surf_view.html')
#app.route('/label_view/<int:scan_number>')
#login_required
def label_view(scan_number):
a, b, c, d = volume_views_setup(scan_number, resource_type='atlas')
if os.path.exists(a):
return render_template('brain_db/scan_view.html')
When I try to run my test, I get
FAIL: test_labeled_scan (test.UserTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1297, in patched
arg = patching.__enter__()
File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1353, in __enter__
self.target = self.getter()
File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1523, in <lambda>
getter = lambda: _importer(target)
File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1210, in _importer
thing = _dot_lookup(thing, comp, import_path)
File "/usr/local/lib/python2.7/site-packages/mock/mock.py", line 1199, in _dot_lookup
__import__(import_path)
File "/usr/src/app/flask_brain_db/brain_db/views.py", line 36, in <module>
#app.route('/surface_test')
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1250, in decorator
self.add_url_rule(rule, endpoint, f, **options)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 66, in wrapper_func
return f(self, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1221, in add_url_rule
'existing endpoint function: %s' % endpoint)
AssertionError: View function mapping is overwriting an existing endpoint function: surface_test
I understand what this error is trying to communicate. Somehow my patch decorator is, in an import, re-executing code that defines the routes in views.py, leading to the error. (The error doesn't appear without the patch decorator and mock function). But I don't understand why, or how I am supposed to patch my helper method. I have really struggled with understanding the correct way to refer to the method that needs patching, but looking at this question for example I think I'm doing it right.

Django Unit Testing: cannot mock django.core.urlresolvers.resolve

I'm trying to mock the django.core.urlresolvers.resolve function, but it doesn't seem to be work. I've tested with custom functions and it works like a charm, but mock ignores resolve completely.
Code:
test_something.py:
class SomethingTestCase(TestCase)
def setUp(self):
self.middleware = SomeMiddleware()
self.request = Mock()
self.request.session = {}
#patch('django.core.urlresolvers.resolve', side_effect=lambda: None)
def test_something(self):
self.assertEqual(self.middleware.process_request(self.request), None)
middleware.py
class SomeMiddleware(object):
def process_request(self, request):
app = resolve(request.path).app_name
print('App name: {}'.format(app))
This results in the following error:
======================================================================
ERROR: test_process_request_unauthenticated (something.tests.unit.test_middleware.SomethingTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/user/virtualenv/something/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "/home/user/projects/something/working/something/tests/unit/test_middleware.py", line 23, in test_process_request_unauthenticated
self.assertEqual(self.middleware.process_request(self.request), None)
File "/home/user/projects/something/working/something/middleware.py", line 14, in process_request
app = resolve(request.path).app_name
File "/home/user/virtualenv/something/local/lib/python2.7/site-packages/django/core/urlresolvers.py", line 534, in resolve
return get_resolver(urlconf).resolve(path)
File "/home/user/virtualenv/something/local/lib/python2.7/site-packages/django/core/urlresolvers.py", line 405, in resolve
raise Resolver404({'path': path})
Resolver404: {u'path': u"<Mock name='mock.path' id='140678271233168'>"}
My goal here is to make the resolve function returns something where I can get the app name.
Why is mock.patch unable to override the resolve function?
Firstly, you are patching it at the wrong location. You should patch it in file it is being used, and not where it is defined as more often than not the target code is imported before the patch is run.
Secondly, if the mocking works it would raise an error something on the lines of NoneType object has no attribute app_name.
#patch(<path_to_middleware.py>, side_effect=lambda: type('mock', (object,), {'app_name': 1})):
def test_something(self):
self.assertEqual(self.middleware.process_request(self.request), None)

How to mock stompest send, and connect methods

I am writing a log handler for ActiveMQ. I have one class, Messenger, to publish a message to ActiveMQ. Also, I added class Handler to handle this and get_logger to get this logger.
import json
import logging
from stompest.config import StompConfig
from stompest.sync import Stomp
class Messanger(object):
def __init__(self, uri):
self.cfg = StompConfig(uri)
def publish(self, queue, data):
data = json.dumps(data)
client = Stomp(self.cfg)
client.connect()
try:
client.send(queue, data)
except Exception, exc:
print "Error: ", exc
client.disconnect()
class Handler(logging.Handler):
def __init__(self, amq_uri, out_queue):
logging.Handler.__init__(self)
self.queue = queue
self.uri = uri
def emit(self, record):
msg = self.format(record)
messanger = Messanger(self.uri)
messanger.send(self.queue, msg)
def get_logger(uri, queue):
logger = logging.getLogger('testlogger')
logger.addHandler(Handler(uri, queue))
return logger
I have written a UT for this.
from unittest import TestCase
from mock import patch, Mock
from my_package import get_logger
class TestHandler(TestCase):
#patch('stompest.sync.client.Stomp')
def test_activemq_handler(self, mock_stomp):
URI = "tcp://localhost:61613"
QUEUE = "/queue/logger_queue"
mock_stomp.connect = Mock(return_value=1)
mock_stomp.send = Mock(return_value=2)
data = "This is test logging"
LOG = get_logger(URI, QUEUE)
LOG.error(data)
But still it goes to the original send method and tries to connect to the server.
Traceback (most recent call last):
File ".my_package/venv/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "./my_package/tests/test_activemq_handler.py", line 23, in test_activemq_handler
LOG.error(data)
File "/System/Library/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1191, in error
self._log(ERROR, msg, args, **kwargs)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1284, in _log
self.handle(record)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1294, in handle
self.callHandlers(record)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1334, in callHandlers
hdlr.handle(record)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 757, in handle
self.emit(record)
File "./my_package/__init__.py", line 28, in emit
messanger.send(self.queue, msg)
File "./my_package/clients/stomp_messanger.py", line 41, in send
client.connect()
File "./my_package/venv/lib/python2.7/site-packages/stompest/sync/client.py", line 85, in connect
for (broker, connectDelay) in self._failover:
File "./my_package/venv/lib/python2.7/site-packages/stompest/protocol/failover.py", line 48, in __iter__
yield broker, self._delay()
File "./my_package/venv/lib/python2.7/site-packages/stompest/protocol/failover.py", line 66, in _delay
raise StompConnectTimeout('Reconnect timeout: %d attempts' % self._maxReconnectAttempts)
StompConnectTimeout: Reconnect timeout: 0 attempts
How can I mock this?
The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.
You are adding the patch to the wrong place. The Messenger class module imports Stomp from stompest.sync before the tests run, and that's why your patching has no effect. You should patch the reference to Stomp in your module.
from unittest import TestCase
from mock import patch, Mock
from my_package import get_logger
class TestHandler(TestCase):
#patch('path.to.your.Messenger_class_module.Stomp') #path to your module
def test_activemq_handler(self, mock_stomp):
URI = "tcp://localhost:61613"
QUEUE = "/queue/logger_queue"
...
This will help if you need further clarification on why this is happening.

Categories

Resources