Reputation: 531
I am writing unittest for a class looks like below. I am trying to assert if logging is properly called with patch keystoneclient. The class looks like below. Problem is, I cannot pass through the for statement and can never get to LOGGER.warning or LOGGER.info even after patching CredentialManager. I am new to whole unittest and Mock so I might not be understanding something clearly.
from keystoneclient.v3.client import Client
from keystoneclient.v3.credentials import CredentialManager
import logging
LOGGER = logging.getLogger(__name__)
class MyClass(object):
def __init__(self):
...
def myfunc(self):
new_credentials = {}
client = Client(
username=self.username,
password=self.password,
auth_url=self.auth_url,
user_domain_name=self.user_domain_name,
domain_name=self.domain_name
)
abc = CredentialManager(client)
for credential in abc.list():
defg = str(credential.type)
(access, secret) = _anotherfunc(credential.blob)
if not defg:
LOGGER.warning('no abc')
if defg in new_credentials:
LOGGER.info('Ignoring duplate')
new_credentials[defg] = (access, secret)
My unit tests looks something like this,
import unittest
from mock import patch, MagicMock
import MyClass
LOGGER = logging.getLogger('my_module')
@patch('MyClass.Client', autospec = True)
@patch('MyClass.CredentialManager', autospec = True)
class TestMyClass(unittest.TestCase):
def test_logger_warning(self,,mock_client, mock_cm):
with patch.object(LOGGER, 'warning') as mock_warning:
obj = MyClass()
mock_warning.assert_called_with('no abc')
The error I am getting looks like this.
for credential in abc.list():
AttributeError: 'tuple' object has no attribute 'list'
So even after patching CredentialManager with autospect, I am getting error on abc.list(). I need to get to the point where I can test LOGGER but it seems like this patching does not work for list(). How should I make this error go away and be able to pass through the for statment so I can assert on logging?
Upvotes: 0
Views: 394
Reputation: 23711
How many details to cover in this answer:
@patch
decorator are applied as stack, that means first decorator -> last mock argumentCredentialManager().list()
return something that contains a empty type
you should instrument your mock_cm
to do itobj.myfunc()
if you want to test something :)The code:
import unittest from mock import patch, Mock import MyClass
@patch('MyClass.Client', autospec=True)
@patch('MyClass.CredentialManager', autospec=True)
class TestMyClass(unittest.TestCase):
#Use decorator for test case too... is simpler and neat
@patch("MyClass.LOGGER", autospec=True)
def test_logger_warning(self, mock_logger, mock_cm, mock_client):
#pay attention to the mock argument order (first local and the the stack of patches
# Extract the mock that will represent you abc
mock_abc = mock_cm.return_value
# Build a mock for credential with desidered empty type
mock_credential = Mock(type="")
# Intrument list() method to return your credential
mock_abc.list.return_value = [mock_credential]
obj = MyClass.MyClass()
# Call the method
obj.myfunc()
# Check your logger
mock_logger.warning.assert_called_with('no abc')
Good point to use autospec=True
for all your patches: it is a good practice.
Anyway I would like encourage you to extract your logging section to a method and test it by some fake credentials: is simpler and a better design: something like
def myfunc(self):
new_credentials = {}
client = Client(
username=self.username,
password=self.password,
auth_url=self.auth_url,
user_domain_name=self.user_domain_name,
domain_name=self.domain_name
)
abc = CredentialManager(client)
for credential in abc.list():
self.process_credential(credential, new_credentials)
@staticmethod
def process_credential(credential, cache):
(access, secret) = _anotherfunc(credential.blob)
defg = str(credential.type)
MyClass.logging_credential_process(credential, not defg, defg in cache)
cache[defg] = (access, secret)
@staticmethod
def logging_credential_process(credential, void_type, duplicate):
if void_type:
LOGGER.warning('no abc')
if duplicate:
LOGGER.info('Ignoring duplate')
is simpler to test and looks better.
Upvotes: 1