子类化 unittest.TestCase,为 `super()__init__()` 提供关键字参数并得到 `__init__ 得到一个意外的关键字参数

Subclassing unittest.TestCase, providing keyword args to `super()__init__()` and getting `__init__ got an unexpected keyword argument

我正在尝试使用 unittest.TestCase 组成 class 层次结构。这些不是 strictly 单元测试我在这里 运行,我试图测试一堆依赖于一些参数的字符串解析功能(客户-name 和 missing-value 例如)。总体思路是简单地利用单元测试的一些便利并保持干燥。

import unittest
import parsingfunc as i

class CustomerTestCase(unittest.TestCase):
    """Base class, most general functionality and test cases"""

    def __init__(self, testname):
        super(CustomerTestCase, self).__init__(testname)

    # helpers

    def mess_with_spaces():
        ...

    def mess_with_case(): 
        ...

    # tests

    def test_case(self, value):
        """
        Test that parsing function produces the same
        value regardless of case of input
        """
        self.assertEqual(self.func(value, missing=self.missing, customer=self.name), 
                         self.func(self.mess_with_case(value), missing=self.missing, customer=self.name)))
        ...

    def test_spaces(self, value):
        """
        Test that parsing function produces the same
        value regardless of spacing present in input
        """
        ...


class AisleValues(CustomerTestCase):
    """Base class for testing aisle values"""

    def __init__(self, testname, customername=None, missing=None):
        super(CustomerTestCase, self).__init__(testname)
        self.name = customername
        self.missing = missing
        self.func = i.aisle_to_num

    ...

class PeacockAisles(AisleValues):
    """Peacock aisle parsing test cases"""

    def __init__(self, testname):
        super(AisleValues, self).__init__(testname, customername='peacock', missing='nan')

    ...

现在尝试创建这些 classes

的实例
In [6]: a = i.CustomerTestCase('space_test')

In [7]: a.__dict__
Out[7]: 
{'_cleanups': [],
 '_resultForDoCleanups': None,
 '_testMethodDoc': '\n        Test that parsing function produces the same\n        value regardless of spacing present in input\n        ',
 '_testMethodName': 'test_spaces',
 '_type_equality_funcs': {list: 'assertListEqual',
  dict: 'assertDictEqual',
  set: 'assertSetEqual',
  frozenset: 'assertSetEqual',
  tuple: 'assertTupleEqual',
  unicode: 'assertMultiLineEqual'}}

In [8]: b = i.AisleValues('test_spaces')

In [9]: b.__dict__
Out[9]: 
{'_cleanups': [],
 '_resultForDoCleanups': None,
 '_testMethodDoc': '\n        Test that parsing function produces the same\n        value regardless of spacing present in input\n        ',
 '_testMethodName': 'test_spaces',
 '_type_equality_funcs': {list: 'assertListEqual',
  dict: 'assertDictEqual',
  set: 'assertSetEqual',
  frozenset: 'assertSetEqual',
  tuple: 'assertTupleEqual',
  unicode: 'assertMultiLineEqual'},
 'func': <function integration.aisle_to_num>,
 'missing': None,
 'name': None}

In [10]: b = i.AisleValues('test_spaces', customername='peacock', missing='nan')

In [11]: b.__dict__
Out[12]: 
{'_cleanups': [],
 '_resultForDoCleanups': None,
 '_testMethodDoc': '\n        Test that parsing function produces the same\n        value regardless of spacing present in input\n        ',
 '_testMethodName': 'test_spaces',
 '_type_equality_funcs': {list: 'assertListEqual',
  dict: 'assertDictEqual',
  set: 'assertSetEqual',
  frozenset: 'assertSetEqual',
  tuple: 'assertTupleEqual',
  unicode: 'assertMultiLineEqual'},
 'func': <function integration.aisle_to_num>,
 'missing': 'nan',
 'name': 'peacock'}

In [13]: c = i.PeacockAisles('test_spaces')

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-d4bba181b94e> in <module>()
----> 1 c = i.PeacockAisles('test_spaces')

/path/to/python.py in __init__(self, testname)
     89 
     90     def __init__(self, testname):
---> 91         super(AisleValues, self).__init__(testname, customername='peacock', missing='nan')
     92         pprint(self.__dict__)
     93 

TypeError: __init__() got an unexpected keyword argument 'customername'

所以怎么回事?谢谢!

您没有正确调用 super。当您在 super 调用中命名 class 时,它应该是 current class,而不是基础 class(除非您真的知道你在做什么,你也想跳过基础 class 的实现,转而使用 "grandparent" class).

您当前的代码有 AisleValues.__init__ 调用 unittest.TestCase.__init__,绕过 CustomerTestCase.__init__。它恰好有效,因为 CustomerTestCase.__init__ 没有做任何有用的事情(你可以删除它但没有任何效果),但这只是运气。当 PeacockAisles.__init__ 调用 CustomerTestCase.__init__(绕过 AisleValues.__init__)时,它会失败,因为祖父母 class 不允许所有与它的 child.[=21 相同的参数=]

你想要:

class AisleValues(CustomerTestCase):
    """Base class for testing aisle values"""

    def __init__(self, testname, customername=None, missing=None):
        super(AisleValues, self).__init__(testname)            # change the class named here
        ...

并且:

class PeacockAisles(AisleValues):
    """Peacock aisle parsing test cases"""

    def __init__(self, testname):
        super(PeacockAisles, self).__init__(testname, customername='peacock', missing='nan')
        ...                                             # and on the previous line