在Python单元测试中模拟和修补keystone客户端



我正在为如下所示的类编写单元测试。我试图断言是否使用补丁keystone客户端正确调用了日志记录。这门课看起来如下。问题是,我无法通过for语句,即使在修补CredentialManager之后,也永远无法访问LOGGER.warning或LOGGER.info。我是整体测试和Mock的新手,所以我可能不太清楚。

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)

我的单元测试看起来像这样,

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')

我得到的错误看起来是这样的。

    for credential in abc.list():
AttributeError: 'tuple' object has no attribute 'list'

因此,即使在用autospect修补CredentialManager之后,我在abc.list()上也会出现错误。我需要达到可以测试LOGGER的程度,但这个补丁似乎对list()不起作用。我应该如何消除这个错误,并能够通过for语句,以便在日志记录中断言?

这个答案要包含多少细节:

  1. 补丁顺序:@patch装饰器作为堆栈应用,这意味着第一个装饰器->最后一个模拟参数
  2. 如果您希望CredentialManager().list()返回包含空type的内容,则应使用mock_cm进行操作
  3. 如果你想测试一些东西,你应该调用obj.myfunc():)

代码:

导入单元测试来自mock导入补丁,mock导入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')

autospec=True用于所有补丁程序是一个很好的做法。

无论如何,我想鼓励你把你的日志部分提取到一个方法中,并用一些假证书来测试它:更简单、更好的设计:类似的东西

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')

测试更简单,看起来更好。

相关内容

  • 没有找到相关文章

最新更新