【问题标题】:Python - assert sub-string of logger output regardless of how the message is constructedPython - 无论消息是如何构造的,都断言记录器输出的子字符串
【发布时间】:2017-09-29 12:10:56
【问题描述】:

我发现了许多关于如何断言已记录的示例,例如 http://www.michaelpollmeier.com/python-mock-how-to-assert-a-substring-of-logger-output

但是我不知道如何将断言与构建消息的特定方式分离。该测试只关心记录的特定 id。

测试代码

mock_logger.warn.assert_called_with(
    all_match(
        contains_string('user-id'), 
        contains_string('team-id')
    )
)

两者都适用

生产代码1(记录器组装消息):

logger.warn(
    "Order for team %s and user %s could not be processed", 
    'team-id', 
    'user-id'
)

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

logger.warn(
    "Order for team {} and user {} could not be processed"
    .format('team-id', 'user-id'), 
    ex
)

这不会按原样工作,但我正在考虑捕获参数或设置自定义日志附加器并对最终消息进行断言。


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

【问题讨论】:

    标签: python unit-testing logging python-mock


    【解决方案1】:

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

    您将all_match 作为first 参数传递给assert_called_with,因此它只能将first 参数与对mock_logger.warn 的调用相匹配。这就是为什么您的测试代码将通过生产代码 2 而不是生产代码 1。

    在生产代码 2 中,warn 的第一个参数是字符串 "Order for team team-id and user user-id could not be processed"。模拟将它的第一个参数传递给all_match,所以它会找到它正在寻找的东西。

    在生产代码 1 中,warn 的第一个参数是 "Order for team %s and user %s could not be processed"。这就是all_match 所知道的一切。第二个和第三个参数包含您希望all_match 查找的字符串,但它无权访问它们。

    不是将匹配器传递给assert_called_with,而是手动检查对模拟的调用将适用于这两种情况。这是我的意思的一个不优雅但可读的实现:

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

    如果您真的想使用匹配器,则必须始终将单个字符串传递给logger.warn,这意味着在调用warn 之前格式化字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-08
      相关资源
      最近更新 更多