Python-不管消息是如何构造的,都要断言记录器输出的子字符串

Python-不管消息是如何构造的,都要断言记录器输出的子字符串,python,unit-testing,logging,python-mock,Python,Unit Testing,Logging,Python Mock,例如,我发现了许多关于如何断言某个日志的示例 但是,我不知道如何将断言与消息的具体构造方式分离。测试只关心记录的特定ID 测试代码 mock_logger.warn.assert_called_with( all_match( contains_string('user-id'), contains_string('team-id') ) ) 应该对两者都有效 生产代码1(记录器汇编消息): 及 生产代码2(我们组装消息并包括异常): 这将

例如,我发现了许多关于如何断言某个日志的示例

但是,我不知道如何将断言与消息的具体构造方式分离。测试只关心记录的特定ID

测试代码

mock_logger.warn.assert_called_with(
    all_match(
        contains_string('user-id'), 
        contains_string('team-id')
    )
)
应该对两者都有效

生产代码1(记录器汇编消息):

生产代码2(我们组装消息并包括异常):


这将无法正常工作,但我正在考虑捕获参数或设置自定义日志追加器,并对最终消息进行断言。



请忽略任何输入错误/潜在的语法错误,因为我没有在IDE中编写代码

如果您希望
warn
方法接受多个参数并格式化字符串本身,我认为像
all\u match
这样的匹配器不起作用。匹配器只匹配一个参数

您将
all\u match
作为
assert\u调用的
的第一个参数传递,因此它只能将第一个参数与
mock\u logger.warn的调用相匹配。这就是为什么测试代码将通过生产代码2而不是生产代码1

在生产代码2中,warn的第一个参数是字符串“无法处理团队id和用户id的订单”。mock将其第一个参数传递给
all\u match
,以便找到它要查找的内容

在生产代码1中,warn的第一个参数是“无法处理团队%s和用户%s的订单”。这就是你所知道的一切。第二个和第三个参数包含要查找的字符串
all\u match
,但它无权访问这些字符串

手动检查对mock的调用对于这两种情况都有效,而不是将matcher传递给
assert\u,并用
调用\u。下面是我的意思的一个不雅但可读的实现:

mock_logger = unittest.Mock()
...
# Call production code
...
calls = mock_logger.warn.call_args_list # gets a list of calls made to the mock

first_call = calls[0] # each call object in call_args_list is a tuple containing 2 tuples: ((positional args), (keyword args)). Let's grab the first one.

arguments = first_call[0] # the positional arguments are the first tuple in the call

if len(arguments) == 1: # If warn got 1 argument, it's a string. Look for 'team-id' and 'user-id' in that argument
    self.assertIn('team-id', arguments[0])
    self.assertIn('user-id', arguments[0])
elif len(arguments) == 3: # if warn got 3 arguments, 'team-id' and 'user-id' should have been the 2nd and 3rd arguments.
    self.assertEqual("Order for team %s and user %s could not be processed", arguments[0])
    self.assertEqual('team-id', arguments[1])
    self.assertEqual('user-id', arguments[2])
如果确实要使用匹配器,则必须始终将单个字符串传递给
记录器。warn
,这意味着在调用
warn
之前格式化字符串

logger.warn(
    "Order for team {} and user {} could not be processed"
    .format('team-id', 'user-id'), 
    ex
)
mock_logger = unittest.Mock()
...
# Call production code
...
calls = mock_logger.warn.call_args_list # gets a list of calls made to the mock

first_call = calls[0] # each call object in call_args_list is a tuple containing 2 tuples: ((positional args), (keyword args)). Let's grab the first one.

arguments = first_call[0] # the positional arguments are the first tuple in the call

if len(arguments) == 1: # If warn got 1 argument, it's a string. Look for 'team-id' and 'user-id' in that argument
    self.assertIn('team-id', arguments[0])
    self.assertIn('user-id', arguments[0])
elif len(arguments) == 3: # if warn got 3 arguments, 'team-id' and 'user-id' should have been the 2nd and 3rd arguments.
    self.assertEqual("Order for team %s and user %s could not be processed", arguments[0])
    self.assertEqual('team-id', arguments[1])
    self.assertEqual('user-id', arguments[2])