【问题标题】:Is it possible to make Nose only run tests which are sub-classes of TestCase or TestSuite (like unittest.main())是否可以让 Nose 只运行作为 TestCase 或 TestSuite 子类的测试(如 unittest.main())
【发布时间】:2010-06-24 14:37:53
【问题描述】:

我的测试框架目前基于一个 test-runner 实用程序,它本身是从 Eclipse pydev python test-runner 派生的。我正在改用 Nose,它具有我的自定义测试运行程序的许多功能,但似乎是质量更好的代码。

我的测试套件包括许多以前从未运行过的抽象测试类。标准的 python 测试运行程序(和我的自定义运行程序)仅运行 unittest.TestCase 和 unittest.TestSuite 的实例。

我注意到,自从我切换到 Nose 后,它几乎可以运行任何以名称“test”开头的东西,这很烦人......因为我们用于 test-mixins 的命名约定也看起来像 Nose 的测试类.以前这些从未作为测试运行,因为它们不是 TestCase 或 TestSuite 的实例。

显然,我可以重新命名方法以从它们的名称中排除“测试”一词……这需要一段时间,因为测试框架非常大并且有很多继承。另一方面,如果有一种方法可以让 Nose 只将 TestCases 和 TestSuites 视为可运行的......而不是其他任何东西,那就太好了。

这个可以吗?

【问题讨论】:

    标签: python unit-testing nose


    【解决方案1】:

    您可以尝试使用nosetests-m 选项。来自文档:

    测试类是定义在 匹配 testMatch 的测试模块或 是 unittest.TestCase 的子类

    -m 设置 testMatch,这样您就可以禁用以 test 开头的任何测试。

    另一件事是您可以将__test__ = False 添加到您的测试用例类声明中,以将其标记为“不是测试”。

    【讨论】:

    • __test__ = False 技巧在父类的情况下不是很有用,这会强制子类显式指定 __test__ = True 否则会被忽略,这是一个危险的技巧。
    【解决方案2】:

    如果你想要一个真正抽象的测试类,你可以从对象继承抽象类,然后在测试用例中继承。

    例如:

    class AbstractTestcases(object):
        def test_my_function_does_something(self):
            self.assertEquals("bar", self.func())
    

    然后使用它:

    class TestMyFooFunc(AbstractTestcases, unittest.TestCase):
        def setUp(self):
            self.func = lambda: "foo"
    

    那么nosetests 将只获取TestMyFooFunc 中的测试用例,而不是AbstractTestCases 中的测试用例。

    【讨论】:

    • 这是避免问题的好方法,但抽象测试在我的 IDE 中非常难以阅读。
    【解决方案3】:

    您可以使用nose 的--attr 参数来指定unittest.TestCase 具有的属性。例如,我使用:

    nosetests --attr="assertAlmostEqual"
    

    使用 andor 匹配可以更加小心:

    nosetests -A "assertAlmostEqual or addTest"
    

    有关方法/属性的完整列表,请参阅 unittest's documentation,以及 Nose 的 description of the capabilities of the --attr plugin

    【讨论】:

    • 这不是最漂亮的解决方案,但与其他建议相比,它确实有效。谢谢!
    【解决方案4】:

    @nailxx 答案的一个附录:

    您可以在父类中设置__test__ = False,然后使用元类(参见This question 的一些精彩解释)在子类化时将其设置回True。

    (终于找到了使用元类的借口!)

    虽然__test__ 是一个双下划线属性,但我们必须将其显式设置为True,因为不设置它会导致python 只是进一步查找MRO 的属性并将其评估为False

    因此,我们需要在类实例化时检查其中一个父类是否有__test__ = False。如果是这种情况,并且当前类定义本身没有设置__test__,我们将在属性字典中添加'__test__': True

    生成的代码如下所示:

    class TestWhenSubclassedMeta(type):
        """Metaclass that sets `__test__` back to `True` when subclassed.
    
        Usage:
    
            >>> class GenericTestCase(TestCase, metaclass=TestWhenSubclassed):
            ...     __test__ = False
            ...
            ...     def test_something(self):
            ...         self.fail("This test is executed in a subclass, only.")
            ...
            ...
            >>> class SpecificTestCase(GenericTestCase):
            ...     pass
    
        """
    
        def __new__(mcs, name, bases, attrs):
            ATTR_NAME = '__test__'
            VALUE_TO_RESET = False
            RESET_VALUE = True
    
            values = [getattr(base, ATTR_NAME) for base in bases
                      if hasattr(base, ATTR_NAME)]
    
            # only reset if the first attribute is `VALUE_TO_RESET`
            try:
                first_value = values[0]
            except IndexError:
                pass
            else:
                if first_value == VALUE_TO_RESET and ATTR_NAME not in attrs:
                    attrs[ATTR_NAME] = RESET_VALUE
    
            return super().__new__(mcs, name, bases, attrs)
    

    可以将此扩展为一些更隐含的行为,例如“如果名称以 Abstract 开头,则自动设置 __test__ = False”,但为了清楚起见,我自己会保留显式分配。


    让我粘贴简单的单元测试来演示行为 - 并提醒每个人在引入功能后应该花两分钟时间测试他们的代码。

    from unittest import TestCase
    
    from .base import TestWhenSubclassedMeta
    
    
    class SubclassesTestCase(TestCase):
        def test_subclass_resetted(self):
            class Base(metaclass=TestWhenSubclassedMeta):
                __test__ = False
    
            class C(Base):
                pass
    
            self.assertTrue(C.__test__)
            self.assertIn('__test__', C.__dict__)
    
        def test_subclass_not_resetted(self):
            class Base(metaclass=TestWhenSubclassedMeta):
                __test__ = True
    
            class C(Base):
                pass
    
            self.assertTrue(C.__test__)
            self.assertNotIn('__test__', C.__dict__)
    
        def test_subclass_attr_not_set(self):
            class Base(metaclass=TestWhenSubclassedMeta):
                pass
    
            class C(Base):
                pass
    
            with self.assertRaises(AttributeError):
                getattr(C, '__test__')
    

    【讨论】:

      【解决方案5】:

      您还可以在测试用例级别使用多重继承,并让基类仅从object 继承。见this thread

      class MyBase(object):
         def finishsetup(self):
             self.z=self.x+self.y
      
         def test1(self):
             self.assertEqual(self.z, self.x+self.y)
      
         def test2(self):
             self.assert_(self.x > self.y)
      
      class RealCase1(MyBase, unittest.TestCase):
         def setUp(self):
             self.x=10
             self.y=5
             MyBase.finishsetup(self)
      
      class RealCase2(MyBase, unittest.TestCase):
         def setUp(self):
             self.x=42
             self.y=13
             MyBase.finishsetup(self) 
      

      【讨论】:

        猜你喜欢
        • 2013-09-20
        • 1970-01-01
        • 1970-01-01
        • 2018-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        相关资源
        最近更新 更多