带有参数的测试装饰器总是被执行

Posted

技术标签:

【中文标题】带有参数的测试装饰器总是被执行【英文标题】:Test decorators with arguments are always executed 【发布时间】:2015-10-16 06:36:17 【问题描述】:

我想要实现的是将某些测试所需的设置代码放入装饰器函数中。我故意避免 setUptearDown 方法,因为它们由测试用例中的所有测试共享,这不是我想要的。这更多的是个人喜好和学习兴趣的问题,而不是直接的解决方案。

简化版代码:

# some import lines
from some.package import interceptor

class TestWhatever(unittest.TestCase):

    def test_simple_thing(self):
        # simple stuff

    def test_another_simple_thing(self):
        # more simple stuff

    @interceptor(arg1, arg2)
    def test_with_some_preparation_needed(self):
        # hey! Really cool stuff in here

    print('This will be executed with every test')

    @interceptor(arg1, arg2, namedarg1='foo', namedarg2='bar')
    def test_with_preparation_also(self):
        # the first decorator is executed
        # sure I'm not following Python's Zen

装饰器功能:

from wsgi_intercept import requests_intercept
from wsgi_intercept import add_wsgi_intercept


def interceptor(cityname, response, host='localhost', port=8000):
    requests_intercept.install()
    add_wsgi_intercept(host, port,
                       lambda: create_webservice(cityname, response))

    def decorator(test):

        def runtest(self):
            test(self)
            requests_intercept.uninstall()

        return runtest

    return decorator


def create_webservice(cityname, response):

    def app(environ, start_response):
        # a simple WSGI app

    return app

每个装饰器都是一个带参数的函数调用,因为我需要参数化设置。调用返回测试的真实装饰器。问题是它们就像放置在函数定义之间的任何其他语句一样,因此它们会被执行。

像我这样用参数测试装饰器的想法可行吗?也许有了数据提供者就可以了。

【问题讨论】:

函数在模块加载时被修饰。所以,是的,mydecorator(arg1, arg2)mydecorator(arg1, arg2, namedarg1='foo', namedarg2='bar') 表达式总是被执行。向我们展示您的实际装饰器,以便我们能够帮助您找出仅在执行 test_with_some_preparation_neededtest_with_preparation_also 方法时才执行的代码。 print() 调用是类主体的一部分,所以是的,它也会被执行。我实际上不确定您的问题是什么; print() 函数与这里的装饰器无关。 print() 行只是我所说的“就像放置在函数定义之间的任何其他语句一样,因此它们被执行”时的一个示例。 我尝试将装饰器函数放在测试类内部、外部和导入的模块中,结果是一样的。我知道我可以在需要时通过在测试的开始和结束时调用一些实用程序函数来解决这个问题,但是装饰器方法对我来说似乎更干净。 我强烈建议放弃 unittest 并改用 py.test,因为它有 fixtures。如果您在重新打开时 ping 我,我可以将其扩展为答案。 【参考方案1】:

将您希望在每个测试中执行的所有内容移动到runtest 包装器中。 interceptor() 装饰器工厂和decorator 本身都会在创建TestWhatever 类对象时执行

def interceptor(cityname, response, host='localhost', port=8000):    
    def decorator(test):
        def runtest(self):
            requests_intercept.install()
            add_wsgi_intercept(
                host, port,
                lambda: create_webservice(cityname, response))
            test(self)
            requests_intercept.uninstall()
        return runtest    
    return decorator

请注意,正常模式仍然使用setUptearDown,并且将那些需要特定设置的测试放在该类中。您可以随时添加更多 TestCase 类,而无需为其他测试设置该设置:

class TestWhateverWithSetup(unittest.TestCase):
    def setUp(self):
        requests_intercept.install()
        add_wsgi_intercept(
            host, port,
            lambda: create_webservice(cityname, response))

    def tearDown(self):
        requests_intercept.uninstall()

    def test_with_some_preparation_needed(self):
        # hey! Really cool stuff in here

    def test_with_preparation_also(self):
        # the first decorator is executed
        # sure I'm not following Python's Zen


class TestWhateverWithoutSetup(unittest.TestCase):
    def test_simple_thing(self):
        # simple stuff

    def test_another_simple_thing(self):
        # more simple stuff

【讨论】:

感谢@MartijnPieters。整洁的。我会按照你的建议去做。无论如何,了解py.test 很有趣,我将进一步探索其他选择。

以上是关于带有参数的测试装饰器总是被执行的主要内容,如果未能解决你的问题,请参考以下文章

python -- 装饰器

Python 带有参数的装饰器

python装饰器

Python装饰器

装饰器复习

基于 Python 类的装饰器,带有可以装饰方法或函数的参数