在nosetests中有条件跳过TestCase装饰器

时间:2014-02-21 13:51:14

标签: python testing nose

有没有办法根据使用nosetests的自定义条件跳过整个TestCase?我指的是unittest.skip*风格的东西。

我试过

import unittest

@unittest.skip("No reason")
class TestFoo(object):
    def test_foo(self):
        assert False

我发现这可以使用python< = 2.7.3(显然是偶然的),但在python 2.7.6中没有。

有没有鼻子测试方法,或者我必须创建自己的装饰器?

注意:

  • 我们尝试了python 2.7.3,2.7.6和nosetests 1.1.2,1.3.0的所有组合。
  • 如果该类继承自unittest.TestCase,那么它可以正常工作,但这不是我需要的。
  • setUpClass提升SkipTest似乎有效,但看起来很笨拙。
  • 我找到了nottest装饰器,但它没有将测试标记为跳过。

摘要

  • 更新20. 5. 2014:到目前为止,我还没有找到任何解决此问题的方法,因此似乎唯一的选择是编写自定义装饰器。
  • 更新12. 6. 2014:我发现在SkipTest中提出setUpClass在某些情况下并不是一个好主意,因为在这些情况下,测试不会teardownContext。如果涉及插件,这可能会产生不利影响。

2 个答案:

答案 0 :(得分:9)

我观察到了同样的行为,unittest.skipunittest.skipIf等等。使用nose运行我的测试时,装饰器不受尊重。

Bakuriu建议编写一个在setUpClass方法中引发SkipTest异常的装饰器解决了这个问题:无论是从unittest.main还是从nose运行,现在都可以正确跳过测试。

这是代码,主要基于unittest装饰器源代码。关键行是在TestCase类上使用装饰器的时候:

from unittest import SkipTest, TestCase
import functools
import types

def _id(obj):
    return obj

def skip(reason):
    """Unconditionally skip a test."""
    def decorator(test_item):
        if not isinstance(test_item, (type, types.ClassType)):
            @functools.wraps(test_item)
            def skip_wrapper(*args, **kwargs):
                raise SkipTest(reason)
            test_item = skip_wrapper
        elif issubclass(test_item, TestCase):
            @classmethod
            @functools.wraps(test_item.setUpClass)
            def skip_wrapper(*args, **kwargs):
                raise SkipTest(reason)
            test_item.setUpClass = skip_wrapper
        test_item.__unittest_skip__ = True
        test_item.__unittest_skip_why__ = reason
        return test_item
    return decorator

def skipIf(condition, reason):
    """Skip a test if the condition is true."""
    if condition:
        return skip(reason)
    return _id

答案 1 :(得分:5)

from nose.plugins.skip import SkipTest

@SkipTest
def execute_main_test():
    model_small = os.path.join(utils.get_project_root(),
                               "models",
                               "small-baseline")
    view.main(True, model_small, False, 31, False, 'mysql_online')
    view.main(False, model_small, False, 31, False, 'mysql_online')
    view.main(False, model_small, True, 31, False, 'mysql_online')
    view.main(False, model_small, False, 31, True, 'mysql_online')