我正在测试一个写入日志文件的函数(具体是它写入日志文件并不重要,它可能正在做任何事情,它只是引起这个问题的原因)
这样的事情:
def do_stuff():
with open('/tmp/mylogs.txt', 'a') as f:
f.write(str(time.time()))
f.write(' stuff done! \n')
return 42
我可以测试一下这个:
def test_doing_stuff(watch_logs):
assert do_stuff() == 42
assert do_stuff() == 43
出于调试目的,当测试失败时,我希望能够打印出新的日志记录 - 我可以像这样制作一个夹具:
@pytest.fixture()
def watch_logs(request):
with open('/tmp/mylogs.txt') as f:
log_before = f.read()
def get_new_logs():
with open('/tmp/mylogs.txt') as f:
log_after = f.read()
return log_after.replace(log_before, '')
return get_new_logs
太棒了 - 现在我可以在测试中的任何一点检查日志内容:
def test_doing_stuff(watch_logs):
assert do_stuff() == 42
print(watch_logs())
assert do_stuff() == 43
print(watch_logs())
嗯 - 啊,但第二次打印不起作用,它是在测试失败后。
如果我的测试夹具总是在测试结束时打印出日志怎么办?然后pytest的stdout捕获会在它失败时显示给我,但不会在它通过时显示它!
@pytest.fixture()
def watch_logs(request):
with open('/tmp/mylogs.txt') as f:
log_before = f.read()
def get_new_logs():
with open('/tmp/mylogs.txt') as f:
log_after = f.read()
return log_after.replace(log_before, '')
def print_new_logs():
print('~' * 20 + ' logs ' + '~' * 20)
print(get_new_logs())
print('~' * 50)
request.addfinalizer(print_new_logs)
return get_new_logs
哦,但是这不起作用,因为在测试终结器期间没有发生pytests的日志捕获。
所以问题是:如何制作可以打印东西的测试终结器?
这是一个超级最小的要点,没有(无关的)写入日志文件的东西:https://gist.github.com/hjwp/5154ec40a476a5c01ba6
答案 0 :(得分:3)
没有记录或干净的方法来实现它,但这是一个黑客:
# conftest.py
def pytest_runtest_call(item):
if hasattr(item, "_request"):
if hasattr(item._request, "_addoutput_on_failure"):
item._request._addoutput_on_failure()
# test_x.py
import pytest
@pytest.fixture
def print_on_fail(request):
def add():
print ("test failed")
request._parent_request._addoutput_on_failure = add
def test_doing_stuff(print_on_fail):
assert False
我们可以考虑一个合适的request.addcall_on_failure(callback)
API。
使yield_fixture
案例工作需要一些内部可能非平凡的重构。
答案 1 :(得分:2)
感谢Holger自己的帮助(感谢@ hpk42!),我有一些有用的东西。只有轻微的魔力/ hacky。
解决方案是使用名为pytest_pyfunc_call
的py.test挂钩,以及名为hookwrapper
的装饰器。它们为我提供了一种方法,可以在测试运行之前和之后挂钩一些代码,但也不会受到stdout劫持的影响。
我们在 conftest.py 中定义了一个新函数:
# conftest.py
@pytest.mark.hookwrapper
def pytest_pyfunc_call(pyfuncitem):
yield
print('this happens after the test runs')
if 'watch_logs' in pyfuncitem.funcargs:
print(pyfuncitem.funcargs['watch_logs']())
现在,如果pytest发现任何使用watch_logs
夹具的测试,它将在测试运行后打印其输出。