Python decorator staticmethod object not callable - python

Hi I'm trying to create a decorator but I'm getting an error of staticmethod object not callable below is my code
from db.persistence import S3Mediator
from sqlalchemy.ext.declarative import declarative_base
import logging
from functools import wraps
Base = declarative_base()
def s3(func):
#wraps(func)
def wrapper(*args, **kwargs):
try:
s3client = S3Mediator.get_s3_connection()
kwargs["s3client"] = s3client
retval = func(*args, **kwargs) #### an error is raised here
except Exception as e:
raise e
return retval
return wrapper
And here is the mediator that instantiate the s3 object
import boto3
import logging
class S3Mediator(object):
s3_client = None
def __init__(self, host, access_key, secret):
self.client = boto3.client(
's3',
aws_access_key_id= access_key,
aws_secret_access_key= secret
)
S3Mediator.s3_client = self.client
#staticmethod
def get_s3_connection():
return S3Mediator.s3_client
Now S3Mediator is already instantiate at the app.py now I'm trying to use this decorator as
#s3
#staticmethod
def s3_connect(s3client):
# code don't reach here. An error is thrown
# do something here
Any idea why its returning a staticmethod object not callable and how to fix this

Ok found the cause of the problem. I put the #staticmethod below my decorator that's why my decorator is thinking that all method of the decorator is static. I just change the
#s3
#staticmethod
def s3_connect(s3client):
# code don't reach here. An error is thrown
# do something here
to this
#staticmethod
#s3
def s3_connect(s3client):
# code don't reach here. An error is thrown
# do something here

Related

Mocking kubernetes client for Python unittest creating AttributeError

I was mocking a function that is used to read k8s secret to fetch secret token. But running unittest is creating error - AttributeError: <module 'kubernetes.client' from '/usr/lib/python3.6/site-packages/kubernetes/client/init.py'> does not have the attribute 'read_namespaced_secret()' I have gone through How do you mock Python Kubernetes client CoreV1Api , but its also not helping my case. Can anyone point out what I am doing wrong here?
My script - read_sec.py
import base64
from kubernetes import client, config
from logger import logger
class kubernetesServices():
def __init__(self):
pass
def get_secret_vault_token(self):
try:
config.load_kube_config()
api_instance = client.CoreV1Api()
sec = api_instance.read_namespaced_secret("random-sec", "random-ns").data
token = base64.b64decode(sec['token']).decode("utf-8")
return token
except Exception as e:
logger.error("got error at get_secret_vault_token: {}".format(str(e)))
Unittest - test_read_sec.py
import unittest
from unittest.mock import patch
from read_sec import *
class MockKubernetes():
def __init__(self):
pass
def mocker_read_namespaced_secret(*args, **kwargs):
class MockReadns():
def __init__(self, json_data):
self.json_data = json_data
def json(self):
return self.json_data
return MockReadns({"data":{"token":"abc123"}})
class TestkubernetesServices(unittest.TestCase):
#patch("kubernetes.client",side_effect=MockKubernetes)
#patch("kubernetes.config",side_effect=MockKubernetes)
#patch("kubernetes.client.read_namespaced_secret()",side_effect=mocker_read_namespaced_secret)
def test_get_secret_vault_token(self,mock_client,mock_config,mock_read):
k8s = kubernetesServices()
token = k8s.get_secret_vault_token()
You need to mock kubernetes.client.CoreV1Api instead of kubernetes.client. Here is an example:
import base64
import unittest
from unittest.mock import patch, Mock
import requests
from kubernetes import client, config
class kubernetesServices():
def get_secret_vault_token(self):
config.load_kube_config()
api_instance = client.CoreV1Api()
sec = api_instance.read_namespaced_secret('random-sec', 'random-ns').data
token = base64.b64decode(sec['token']).decode('utf-8')
return token
class TestkubernetesServices(unittest.TestCase):
#patch(
'kubernetes.client.CoreV1Api',
return_value=Mock(read_namespaced_secret=Mock(return_value=Mock(data={'token': b'YWJjMTIz'})))
)
#patch('kubernetes.config.load_kube_config', return_value=Mock())
def test_get_secret_vault_token(self, mock_client, mock_config):
k8s = kubernetesServices()
token = k8s.get_secret_vault_token()
self.assertEqual(token, 'abc123')
Result:
---------------------------------------------------------------------
Ran 1 tests in 0.071s
PASSED (successes=1)
JFYI: side_effect better to use when you need multiple results. Example:
class TestRequest(unittest.TestCase):
def test_side_effect(self):
with patch('requests.get', side_effect=[1, 2, 3]):
print(requests.get('url1')) # 1
print(requests.get('url2')) # 2
print(requests.get('url3')) # 3

Mocking when inheriting from pyserial

I have a class that inherits from pyserial's serial.Serial class that looks something like this:
class SYM1(serial.Serial):
def __init__(self, *args, debug=None, baudrate=4800, timeout=1, **kwargs):
super().__init__(*args, baudrate=baudrate, timeout=timeout, **kwargs)
LOG.info('using port %s', self.port)
def read(self, count):
...
return super().read(count)
def write(self, data):
...
return super().write(data)
def connect(self):
...
def send_command(self, cmd, *args):
...
I would like to write unit tests for the class, but I'm unsure how best to set things up such that (a) methods defined on the SYM1 class are callable normally, while (b) methods inherited from serial.Serial are mocked out.
I started with this:
import pytest
import serial
from unittest import mock
serial.Serial = mock.MagicMock
from symtool import symtool # NOQA
#pytest.fixture
def sym(monkeypatch):
s = symtool.SYM1('TESTDEV')
s.connect()
return s
def test_connect(sym):
pass
But that fails here:
LOG.info('using port %s', self.port)
With:
AttributeError: Mock object has no attribute 'port'
and that puzzles me, because if it's a mock.Mock object, I would expect:
>>> from unittest import mock
>>> m = mock.Mock()
>>> m.port
<Mock name='mock.port' id='139910315631568'>
It's not just simple attributes; if I comment out the line causing the first AttributeError, it will instead fail later on when calling super().write:
AttributeError: 'super' object has no attribute 'write'
What's going on here, and what's the right way to do it?

Flask unit tests -- mocking up Aerospike DB

I have the following endpoint,
#developer_blueprint.route("/init_db", methods=["POST"])
def initialize_database():
try:
upload_data(current_app)
logger.debug("Database entries upload.")
return jsonify({"result": "Database entries uploaded."}), 201
except Exception as e:
return jsonify({"error": str(e)})
def upload_data(app):
with open("src/core/data/data.json") as data_file:
data = json.load(data_file)
try:
current_app.db.put(("somenamespace", "test", "default"), data, None)
except Exception as e:
raise e
I'm trying to figure out how to unit test this (we need to get coverage on our code).
Do I just mock up app.db? How can I do that?
Any suggestions would be appreciated.
It is not uncommon to mock database calls for unit testing using something like unittest.mock and then run Aerospike in a container or VM for end-to-end testing.
However, keep in mind that the Aerospike Python client library is written in C for better performance and thus it is not easy to do partial patching (aka "monkey patching"). For example, you will get a TypeError: can't set attributes of built-in/extension type if you try to simply patch out aerospike.Client.put.
One approach is to create a mock client object to replace or sub-class the Aerospike client object. The implementation of this mock object depends on your code and the cases you are testing for.
Take the following example code in which app.db is an instance of the Aerospike client library:
# example.py
import aerospike
import json
class App(object):
db = None
def __init__(self):
config = {'hosts': [('127.0.0.1', 3000)]}
self.db = aerospike.client(config).connect()
def upload_data(app):
with open("data.json") as data_file:
data = json.load(data_file)
try:
app.db.put(("ns1", "test", "default"), data, None)
except Exception as e:
raise e
if __name__ == "__main__":
app = App()
upload_data(app)
In writing unit tests for the upload_data function let's assume you want to test for a success case which is determined to mean that the put method is called and no exceptions are raised:
# test.py
from unittest import TestCase, main
from unittest.mock import PropertyMock, patch
from example import App, upload_data
from aerospike import Client, exception
class MockClient(Client):
def __init__(self, *args, **kwargs):
pass
def put(self, *args, **kwargs):
return 0
class ExampleTestCase(TestCase):
def test_upload_data_success(self):
with patch.object(App, 'db', new_callable=PropertyMock) as db_mock:
db_mock.return_value = client = MockClient()
app = App()
with patch.object(client, 'put') as put_mock:
upload_data(app)
put_mock.assert_called()
if __name__ == '__main__':
main()
In the test_upload_data_success method the App.db property is patched with the MockClient class instead of the aerospike.Client class. The put method of the MockClient instance is also patched so that it can be asserted that the put method gets called after upload_data is called.
To test that an exception raised by the Aerospike client is re-raised from the upload_data function, the MockClient class can be modified to raise an exception explicitly:
# test.py
from unittest import TestCase, main
from unittest.mock import PropertyMock, patch
from example import App, upload_data
from aerospike import Client, exception
class MockClient(Client):
def __init__(self, *args, **kwargs):
self.put_err = None
if 'put_err' in kwargs:
self.put_err = kwargs['put_err']
def put(self, *args, **kwargs):
if self.put_err:
raise self.put_err
else:
return 0
class ExampleTestCase(TestCase):
def test_upload_data_success(self):
with patch.object(App, 'db', new_callable=PropertyMock) as db_mock:
db_mock.return_value = client = MockClient()
app = App()
with patch.object(client, 'put') as put_mock:
upload_data(app)
put_mock.assert_called()
def test_upload_data_error(self):
with patch.object(App, 'db', new_callable=PropertyMock) as db_mock:
db_mock.return_value = MockClient(put_err=exception.AerospikeError)
app = App()
with self.assertRaises(exception.AerospikeError):
upload_data(app)
if __name__ == '__main__':
main()

Mock a Class, but not one of its functions

When I import MyApp from app.py, a instance of the SerialConnection class
is created immediately. I want to mock the SerialConnection class but I still need a function from within this SerialConnection class.
app.py
# A module that creates strings that is sent via SerialConnection
from myserial import SerialConnection
class MyApp():
global ser
ser = SerialConnection() # ---> Needs to be mocked
def __init__(self):
pass
#ser.decorator # ---> Needs to be by-passed
def myfunc(self):
return 'testing1234'
myserial.py
# A module to write and read to a serial/UART buffer
from functools import wraps
import serial
class SerialConnection():
def __init__(self):
""" Initilize the Serial instance. """
self.ser = serial.Serial(port=2, baudrate=9600)
def decorator(self, func):
#wraps(func)
def wrapper_func(*args):
return func(*args)
return wrapper_func
test_app.py
# This file tests the function "myfunc" from "MyApp" class.
from patch import mock
#patch('myserial.SerialConnection')
def test_myfunc(mock_class):
# I can now import MyApp successfuly...
from app import MyApp
# ..but I need to bypass the decorator myserial.SerialConnection.decorator
# do I add a by-pass decorator to the "mock_class"?
# myfunc() turns out to be mocked and not a real function
assert MyApp().myfunc() == 'testing1234'

Python class mock. How can I get mock-class method calls information

I have a class named Client providing some service by its getResponse method. This class is used by other classes.
I make unit testing for class Driver who uses the Client class.
By using mock.patch, I replace the Client class by mock class called MockClient
which has the same getResponse method returning some predefined values.
It works great. But now I want to test parameters the getRsponse method was called with.
I want to do it by using the *assert_has_calls* method.
Did not find how to do it. Please advice.
Class under test:
# Driver and its Client
class Driver:
def __init__ (self):
self.client = Client()
def call(self, param):
return self.client.getResponse(param)
class Client:
def getResponse(self, param):
return 'original'
This is the Test class with the mock class:
import unittest
import mock
import driver
from driver import Driver
from driver import Client
class MockClient:
def getResponse(self,param):
return 'mock class'
class TestIt(unittest.TestCase):
def setUp(self):
self.mock_client = mock.patch('driver.Client',create=True, new=MockClient)
self.mock_client.start()
def tearDown(self):
self.mock_client.stop()
def test_call(self):
driver = Driver()
result = driver.call('test')
self.assertEqual(result, 'mock class')
assert_has_calls expects a list of call objects that it can compare to.
You can get a call object by calling the mock.call helper function with your expected arguments and keyword arguments.
This example is straight from the documentation and illustrates the usage quite well:
mock = Mock(return_value=None)
mock(1)
mock(2)
mock(3)
mock(4)
calls = [call(2), call(3)]
mock.assert_has_calls(calls)
calls = [call(4), call(2), call(3)]
mock.assert_has_calls(calls, any_order=True)

Categories