【问题标题】:PyTest skip module_teardown()PyTest 跳过 module_teardown()
【发布时间】:2015-10-04 14:07:54
【问题描述】:

我的测试模块中有以下代码

def teardown_module():
    clean_database()
def test1(): pass
def test2(): assert 0

并且我希望只有在某些测试失败时才调用teardown_module()(一些清理代码)。否则(如果全部通过)不应调用此代码。 我可以用 PyTest 做这样的把戏吗?

【问题讨论】:

    标签: python unit-testing pytest


    【解决方案1】:

    你可以。但这有点像黑客。 写在这里:http://pytest.org/latest/example/simple.html#making-test-result-information-available-in-fixtures 您执行以下操作,以设置用于保存测试调用每个阶段的状态的属性:

    # content of conftest.py
    import pytest
    @pytest.mark.tryfirst
    def pytest_runtest_makereport(item, call, __multicall__):
        rep = __multicall__.execute()
        setattr(item, "rep_" + rep.when, rep)
        return rep
    

    在夹具中,您只需检查这些属性的条件,如下所示:

    import pytest
    @pytest.yield_fixture(scope="module", autouse=True)
    def myfixture(request):
        print "SETUP"
        yield
        # probably should not use "_collected" to iterate over test functions
        if any(call.rep_call.outcome != "passed" for call in request.node._collected):
            print "TEARDOWN"
    

    这样,如果与该模块夹具相关的任何测试未“通过”(因此“失败”或“跳过”),则条件成立。

    【讨论】:

    【解决方案2】:

    此处发布的答案和文档链接很有帮助,但不足以满足我的需求。如果模块 (.py) 文件中的任何测试失败,我需要一个模块拆卸函数来独立执行每个模块。

    GitHub 上提供了完整的示例项目

    首先,我们需要一个挂钩来将测试函数结果附加到 测试节点。这直接取自 pytest 文档:

    # in conftest.py
    @pytest.hookimpl(tryfirst=True, hookwrapper=True)
    def pytest_runtest_makereport(item, call):
        # execute all other hooks to obtain the report object
        outcome = yield
        rep = outcome.get_result()
        # set a report attribute for each phase of a call, which can
        # be "setup", "call", "teardown"
        var_name = "rep_" + rep.when
        setattr(item, var_name, rep)
    

    之后,我们需要 另一个 钩子让测试用例找到模块并 将自己存储在那里,以便模块可以轻松找到它的测试用例。可能 有更好的方法,但我找不到。

    # also in conftest.py
    @pytest.fixture(scope="function", autouse=True)
    def _testcase_exit(request):
        yield
        parent = request.node.parent
        while not isinstance(parent, pytest.Module):
            parent = parent.parent
        try:
            parent.test_nodes.append(request.node)
        except AttributeError:
            parent.test_nodes = [request.node]
    

    一旦我们这样做了,很高兴有一个装饰器函数来启动模块 完成查看其测试节点,查找是否有任何故障,以及 那么如果调用了与装饰器相关的函数:

    # also also in conftest.py
    def module_error_teardown(f):
        @wraps(f)
        @pytest.fixture(scope="module", autouse=True)
        def wrapped(request, *args, **kwargs):
            yield
            try:
                test_nodes = request.node.test_nodes
            except AttributeError:
                test_nodes = []
    
            something_failed = False
            for x in test_nodes:
                try:
                    something_failed |= x.rep_setup.failed
                    something_failed |= x.rep_call.failed
                    something_failed |= x.rep_teardown.failed
                except AttributeError:
                    pass
            if something_failed:
                f(*args, **kwargs)
        return wrapped
    

    现在我们拥有了所有必要的框架。现在,带有失败测试用例的测试文件很容易编写:

    from conftest import module_error_teardown
    
    
    def test_something_that_fails():
        assert False, "Yes, it failed."
    
    
    def test_something_else_that_fails():
        assert False, "It failed again."
    
    
    @module_error_teardown
    def _this_gets_called_at_the_end_if_any_test_in_this_file_fails():
        print('')
        print("Here's where we would do module-level cleanup!")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 2021-07-02
      • 2015-03-26
      • 1970-01-01
      • 2019-03-22
      相关资源
      最近更新 更多