繁体   English   中英

如何在py.test终结器中打印内容

[英]How to print stuff in a py.test finalizer

我正在测试一个写入日志文件的函数(它具体写入一个日志文件并不重要,它可以做任何事情,这正是引起这个问题的原因)

像这样:

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

没有记录或明确的方法来实现它,但是这里有一个技巧:

# 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案例起作用需要一些内部的可能不重要的重构。

感谢Holger自己的帮助(感谢@ hpk42!),我有了一些行之有效的方法。 只有一点魔术/ hacky。

解决方案是使用一个名为pytest_pyfunc_call的py.test钩子以及一个名为hookwrapper的装饰器。 它们为我提供了一种方法,可以在测试运行之前和之后挂接一些代码,而且还不受标准输出劫持的影响。

我们在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固定装置的任何测试,它将在测试运行后打印其输出。

完整示例: https//gist.github.com/hjwp/4294f0acbef5345a7d46

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM