为所有派生类运行基类的测试

时间:2014-07-07 10:23:22

标签: python-3.x nose python-unittest

给出一些带有一些多重继承和/或混合的类的层次结构(但你可以称之为)。

我为每个基类和mixin都有一个详细的测试用例(unittest.TestCase)。

如何将这些测试用例重用于从Base类和mixins派生的类?我觉得,测试/断言派生类继承自某些基类并不足以确保它继承基类的特定行为。

我想写一些类似的东西:

class Base(object):
  # provides certain behavior

class Derived(Base, Mixin):
  # does some additional stuff compared to Base and Mixin

class BaseTest(unittest.TestCase):
  # tests on Base's behavior

class DerivedTest(unittest.TestCase):
  def setUp(self):
    self.default = Derived()

  def test_has_behavior_of_base(self):
    # instead of
    self.assertIsInstance(self.default, Base)
    self.assertIsInstance(self.default, Mixin)
    # write
    self.assertPasses(BaseTest, Derived)

是否有 pythonic 方法可以通过Pythons unittest模块实现此目的,并且可能对nose包提供一些帮助?

2 个答案:

答案 0 :(得分:1)

一般情况下,由于 Python 是一种 duck-typed 语言,因此最好尽量减少对显式类型/类的检查(至少如果你需要的话)更多 Pythonic 方法。

由于 duck-typing 主要依赖于行为,而 nose 提供了测试的自动检测,如果您使用目录调用它并相应地命名您的测试类和方法(即包含单词 test 等,我建议如下:

base 类定义一个测试类,测试基类的所有行为。将公共代码分解为非测试方法,比如baseClassCore,它调用实际方法,获取结果,然后返回它们。然后为每个派生的类设置一个测试,该类调用baseClassCore代码并断言预期的结果(如果断言代码对所有这些代码也是通用的,那么也可以将其分解出来并且然后从测试方法调用。)

然后,对于特定的派生的类,为每个仅测试这些类特有的行为的测试类创建一个测试类。

您也可以在派生类测试中针对该特定类型的测试中调用baseClassCore,而不是 base 类测试 - 它是一个偏好问题。

一旦这些都被定义了(其中许多只是围绕核心代码的包装器,因此它们在 JUnit 结果中被拆分为单独的测试并传递所需的特定类型),运行针对整个目录的 nose 应检测并运行它们。如果使用 -v 标志运行,您将在运行时获取方法名称,或者,如果存在,则获取方法签名下方的文档字符串,而不仅仅是状态字母。

请注意,您无需明确使用 unittest 。如果从unittest.TestCase派生测试类,那么 nose 就足以获得与 JUnit 兼容的行为。

顺便说一句,如果您有任何需要的配置(截止日期,使用的服务器等), nose-testconfig 非常方便(https://pypi.python.org/pypi/nose-testconfig)。我通常将它与 JSON 文件一起使用,然后将其输入名为dict的{​​{1}}。

答案 1 :(得分:0)

这是一个已填写的示例。 Square来自Rectangle。如果我们从TestRectangle派生TestSquare,其中TestRectangle派生自unittest.TestCase,那么TestRectangle的所有测试方法也将从TestSquare运行。

import unittest


class Rectangle(object):

    def __init__(self, width, height):
        self._width = width
        self._height = height

    def get_area(self):
        return self.get_width() * self.get_height()

    def get_width(self):
        return self._width

    def get_height(self):
        return self._height

    def set_width(self, width):
        self._width = width

    def set_height(self, height):
        self._height = height


class Square(Rectangle):

    def __init__(self, side):
        self._side = side

    def get_width(self):
        return self._side

    def set_width(self, width):
        self._side = width

    def get_height(self):
        return self._side

    def set_height(self, height):
        self._side = height


class TestRectangle(unittest.TestCase):

    def setUp(self):
        self.default = Rectangle(2, 3)

    def test_area(self):
        self.assertEqual(
            self.default.get_area(),
            self.default.get_width() * self.default.get_height())

    def test_set_get(self):
        self.default.set_width(4)
        self.default.set_height(5)
        self.assertEqual(self.default.get_width(), 4)
        self.assertEqual(self.default.get_height(), 5)

class TestSquare(TestRectangle):

    def setUp(self):
        self.default = Square(3)