在Py.Test中使用funcarg工厂时的参考周期

时间:2013-02-21 05:57:51

标签: python pytest

考虑以下虚拟Resource类:

import pytest


class Resource:

    def __init__(self, param):
        self.param = param
        print "\nResource created", self.param, self

    def __del__(self):
        print "Resource deleted", self.param, self

它模拟了我想在每个测试用例之前创建的一些资源,并在测试用例之后完全取消初始化。使用旧式的funcargs,我写了一个像

这样的测试
def pytest_funcarg__resource_fa(request):
    res = Resource(request.param)
    return res

def pytest_generate_tests(metafunc):
    metafunc.parametrize("resource_fa", [1, 2], indirect=True)

def test_funcarg_resource(resource_fa):
    print "Testcase: resource", resource_fa.param

当我运行py.test -s -v时,我得到以下输出:

============================================ test session starts =============================================
platform linux2 -- Python 2.7.3 -- pytest-2.3.3 -- /usr/bin/python
plugins: cov
collected 2 items 

test_factory.py:48: test_funcarg_resource[1] 
Resource created 1 <test_factory.Resource instance at 0x292cab8>
Testcase: resource 1
PASSED
test_factory.py:48: test_funcarg_resource[2] 
Resource created 2 <test_factory.Resource instance at 0x292cfc8>
Testcase: resource 2
PASSED

========================================== 2 passed in 0.01 seconds ==========================================

如您所见,两个资源的__del__从未被调用过。如果我使用新式灯具,则会出现相同的行为:

@pytest.fixture(scope="function", params=[1, 2])
def resource_fx(request):
    res = Resource(request.param)
    return res

def test_fixture_resource(resource_fx):
    print "Testcase: resource", resource_fx.param

当然,我可以添加一个自定义终结器并强制取消初始化资源中需要取消初始化的任何内容,但这让我觉得有点多余 - 在测试之外,__del__足以确保资源的内部正确发布,我不明白为什么Py.Test需要在测试用例完成时保留对res的任何引用。它是Py.Test的预期行为,还是应该提交错误?

1 个答案:

答案 0 :(得分:2)

依靠__del__最终确定并不是一个好主意,因为它是由垃圾收集周期触发的(根据Python实现,它会有所不同)。使用pytest,您通常从资源/ funcarg工厂调用request.addfinalizer(resource.finalize)以适当调用终结函数。 pytest然后保证完成独立于GC细节。