当 Unittest 没有时,鼻子测试失败
Nose test fails when Unittest does not
我们发现 Python 鼻子测试与单元测试的结果之间存在奇怪的差异。这是导致问题的代码,特别是最后一行:
import logging
class ClassThatLogsBadly:
log = logging.getLogger(__name__)
def method_that_logs_badly(self):
self.log.error('A message', 'Another string')
return ('I ran')
上面的 log
是从 Python 的日志库中检索的,并且由于以下原因在调用时会产生问题(来自 Python 库:logging/__init__.py:328
):
def getMessage(self):
"""
Return the message for this LogRecord.
Return the message for this LogRecord after merging any user-supplied
arguments with the message.
"""
msg = str(self.msg)
if self.args:
msg = msg % self.args
return msg
如你所见,它试图拼接Another string
到A message
,但后者没有有效的插槽(类型%s
)需要填充,所以它做不到。这是一个 known problem,解决方案是在记录器之外格式化你的东西。但是,我们的问题是进行测试以确保完成此操作。
运行 针对使用 unittest 或 py.test 调用日志记录库的代码的测试不会引发失败。 运行 与 nose 相同的测试导致错误,并且测试失败。我们的大部分构建内容都是围绕 py.test 库构建的,没有这个测试失败是令人担忧的:我们不确定这是否是出于某种原因的预期行为,这些库之间处理的根本区别是什么,以及通常该怎么办。
我们正在使用 Python 3.4。这是运行此代码的示例测试:
import logging
import unittest
from mycode import ClassThatLogsBadly
class TestClassThatLogsBadly(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_method_that_logs_badly(self):
clz = ClassThatLogsBadly()
result = clz.method_that_logs_badly()
self.assertEquals('I ran', result)
因为日志记录配置是全局的,为了正确使用日志记录功能,您必须正确配置日志记录。在您的特定情况下,没有在根级别配置处理程序,并且您没有为 self.log
配置处理程序,因此不会发出任何日志消息(只是 "No handlers could be found for logger "mycode")。此消息旨在表明日志记录配置中存在错误,因为您的日志记录树如下所示:
<--""
Level WARNING
|
o<--"mycode"
Level NOTSET so inherits level WARNING
由于您没有处理程序,因此您的 log.error
消息不会执行任何操作,并且当 运行 在 unittest
或 py.test
下时,您的代码不会被执行。
nose
默认情况下会在根级别添加一个日志捕获处理程序,因此最终的日志记录配置已正确配置,并且您的 log.error
代码已正确执行。
一个快速修复方法是在 ClassThatLogsBadly
中的代码之上添加 logging.basicConfig()
或在初始化时向 log
class 成员添加适当的处理程序。这样,unittest 和 py.test 都会捕获它。
最后,你真的不应该在记录器之外格式化你的东西:如果没有配置级别,你根本不应该进行格式化,只是跳过整个日志记录以加快执行速度。
我们发现 Python 鼻子测试与单元测试的结果之间存在奇怪的差异。这是导致问题的代码,特别是最后一行:
import logging
class ClassThatLogsBadly:
log = logging.getLogger(__name__)
def method_that_logs_badly(self):
self.log.error('A message', 'Another string')
return ('I ran')
上面的 log
是从 Python 的日志库中检索的,并且由于以下原因在调用时会产生问题(来自 Python 库:logging/__init__.py:328
):
def getMessage(self):
"""
Return the message for this LogRecord.
Return the message for this LogRecord after merging any user-supplied
arguments with the message.
"""
msg = str(self.msg)
if self.args:
msg = msg % self.args
return msg
如你所见,它试图拼接Another string
到A message
,但后者没有有效的插槽(类型%s
)需要填充,所以它做不到。这是一个 known problem,解决方案是在记录器之外格式化你的东西。但是,我们的问题是进行测试以确保完成此操作。
运行 针对使用 unittest 或 py.test 调用日志记录库的代码的测试不会引发失败。 运行 与 nose 相同的测试导致错误,并且测试失败。我们的大部分构建内容都是围绕 py.test 库构建的,没有这个测试失败是令人担忧的:我们不确定这是否是出于某种原因的预期行为,这些库之间处理的根本区别是什么,以及通常该怎么办。
我们正在使用 Python 3.4。这是运行此代码的示例测试:
import logging
import unittest
from mycode import ClassThatLogsBadly
class TestClassThatLogsBadly(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_method_that_logs_badly(self):
clz = ClassThatLogsBadly()
result = clz.method_that_logs_badly()
self.assertEquals('I ran', result)
因为日志记录配置是全局的,为了正确使用日志记录功能,您必须正确配置日志记录。在您的特定情况下,没有在根级别配置处理程序,并且您没有为 self.log
配置处理程序,因此不会发出任何日志消息(只是 "No handlers could be found for logger "mycode")。此消息旨在表明日志记录配置中存在错误,因为您的日志记录树如下所示:
<--""
Level WARNING
|
o<--"mycode"
Level NOTSET so inherits level WARNING
由于您没有处理程序,因此您的 log.error
消息不会执行任何操作,并且当 运行 在 unittest
或 py.test
下时,您的代码不会被执行。
nose
默认情况下会在根级别添加一个日志捕获处理程序,因此最终的日志记录配置已正确配置,并且您的 log.error
代码已正确执行。
一个快速修复方法是在 ClassThatLogsBadly
中的代码之上添加 logging.basicConfig()
或在初始化时向 log
class 成员添加适当的处理程序。这样,unittest 和 py.test 都会捕获它。
最后,你真的不应该在记录器之外格式化你的东西:如果没有配置级别,你根本不应该进行格式化,只是跳过整个日志记录以加快执行速度。