在鼻子下测试Python代码时应该如何验证日志消息?
我正在尝试编写一个简单的单元测试,该测试将验证在特定条件下,我的应用程序中的类是否会通过标准日志记录 API 记录错误。 我无法弄清楚测试这种情况的最干净的方法是什么。
我知道鼻子已经通过它的日志插件捕获日志输出,但这似乎旨在作为失败测试的报告和调试辅助。
我可以看到的两种方法是:
- 模拟日志记录模块,要么以零碎的方式(mymodule.logging = mockloggingmodule),要么使用适当的模拟库。
- 编写或使用现有的鼻子插件来捕获输出并验证它。
如果我采用前一种方法,我想知道在模拟日志模块之前将全局状态重置为最干净的方法是什么。
期待您对此的提示和技巧......
I'm trying to write a simple unit test that will verify that, under a certain condition, a class in my application will log an error via the standard logging API. I can't work out what the cleanest way to test this situation is.
I know that nose already captures logging output through it's logging plugin, but this seems to be intended as a reporting and debugging aid for failed tests.
The two ways to do this I can see are:
- Mock out the logging module, either in a piecemeal way (mymodule.logging = mockloggingmodule) or with a proper mocking library.
- Write or use an existing nose plugin to capture the output and verify it.
If I go for the former approach, I'd like to know what the cleanest way to reset the global state to what it was before I mocked out the logging module.
Looking forward to your hints and tips on this one...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
从 python 3.4 开始,标准 unittest 库提供了一个新的测试断言上下文管理器,
assertLogs
。 来自 文档:From python 3.4 on, the standard unittest library offers a new test assertion context manager,
assertLogs
. From the docs:更新:不再需要下面的答案。 请使用内置 Python 方式!
这个答案扩展了 https://stackoverflow.com/a/1049375/1286628 中完成的工作。 处理程序基本上是相同的(构造函数更惯用,使用
super
)。 此外,我添加了如何将处理程序与标准库的unittest
一起使用的演示。然后您可以在标准库
unittest.TestCase
中使用处理程序,如下所示:UPDATE: No longer any need for the answer below. Use the built-in Python way instead!
This answer extends the work done in https://stackoverflow.com/a/1049375/1286628. The handler is largely the same (the constructor is more idiomatic, using
super
). Further, I add a demonstration of how to use the handler with the standard library'sunittest
.Then you can use the handler in a standard-library
unittest.TestCase
like so:我曾经模拟记录器,但在这种情况下,我发现最好使用日志记录处理程序,因此我基于 jkp 建议的文档(现已失效,但缓存在互联网档案)
I used to mock loggers, but in this situation I found best to use logging handlers, so I wrote this one based on the document suggested by jkp(now dead, but cached on Internet Archive)
最简单的答案是
Pytest 有一个名为
caplog
的内置装置。 无需设置。我希望在浪费 6 小时之前就了解 caplog。
就我个人而言,我希望我的控制台输出干净,所以我喜欢这样让 log-to-stderr 保持沉默:
如果你厌倦了可怕的单元测试代码,那么 pytest 装置有很多值得喜欢的地方。
Simplest answer of all
Pytest has a built-in fixture called
caplog
. No setup needed.I wish I'd known about caplog before I wasted 6 hours.
Personally, I want my console output clean, so I like this to silence the log-to-stderr:
There is a lot to like about pytest fixtures if you're sick of horrible unittest code.
Brandon 的回答:
snippet:
注意:上面的内容与调用 nosetests 并获取该工具的 logCapture 插件的输出并不冲突
Brandon's answer:
snippet:
Note: the above does not conflict with calling nosetests and getting the output of logCapture plugin of the tool
作为 Reef 回答的后续,我随意使用 pymox 编写了一个示例。
它引入了一些额外的辅助函数,使存根函数和方法变得更加容易。
As a follow up to Reef's answer, I took a liberty of coding up an example using pymox.
It introduces some extra helper functions that make it easier to stub functions and methods.
如果您定义这样的辅助方法:
那么您可以编写如下测试代码:
If you define a helper method like this:
Then you can write test code like this:
您应该使用模拟,因为有一天您可能想将您的记录器更改为数据库记录器。 如果它在鼻子测试期间尝试连接到数据库,您将不会高兴。
即使标准输出被抑制,模拟也会继续工作。
我使用了 pyMox 的存根。 请记住在测试后取消设置存根。
You should use mocking, as someday You might want to change Your logger to a, say, database one. You won't be happy if it'll try to connect to the database during nosetests.
Mocking will continue to work even if standard output will be suppressed.
I have used pyMox's stubs. Remember to unset the stubs after the test.
在tornado中实现的
ExpectLog
类是一个很棒的实用程序:http://tornado.readthedocs.org/en/latest/_modules/tornado/testing.html#ExpectLog
The
ExpectLog
class implemented in tornado is a great utility:http://tornado.readthedocs.org/en/latest/_modules/tornado/testing.html#ExpectLog
关闭@Reef的答案,我确实尝试了下面的代码。 它对于 Python 2.7(如果您安装 mock)和 Python 3.4 都适用。
Keying off @Reef's answer, I did tried the code below. It works well for me both for Python 2.7 (if you install mock) and for Python 3.4.