【问题标题】:Deriving a class from TestCase throws two errors从 TestCase 派生一个类会引发两个错误
【发布时间】:2011-09-27 00:04:54
【问题描述】:

我有一些基本的设置/拆卸代码,我想在一大堆单元测试中重用它们。所以我有了创建一些派生类以避免在每个测试类中重复代码的好主意。

这样做时,我收到了两个奇怪的错误。一,我解决不了。这是一个无法解决的问题:

AttributeError: 'TestDesktopRootController' object has no attribute '_testMethodName'

这是我的基类:

import unittest
import twill
import cherrypy
from cherrypy._cpwsgi import CPWSGIApp


class BaseControllerTest(unittest.TestCase):

    def __init__(self):
        self.controller = None

    def setUp(self):
        app = cherrypy.Application(self.controller)

        wsgi = CPWSGIApp(app)

        twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi)

    def tearDown(self):
        twill.remove_wsgi_intercept('localhost', 8080)

这是我的派生类:

import twill
from base_controller_test import BaseControllerTest

class TestMyController(BaseControllerTest):

    def __init__(self, args):
        self.controller = MyController()
        BaseControllerTest.__init__(self)

    def test_root(self):
        script = "find 'Contacts'"
        twill.execute_string(script, initial_url='http://localhost:8080/')

另一个奇怪的错误是:

TypeError: __init__() takes exactly 1 argument (2 given)

对此的“解决方案”是将单词“args”添加到派生类中的__init__ 函数中。有什么办法可以避免吗?

记住,我有两个错误。

【问题讨论】:

  • 你为什么要覆盖__init__()?一般来说,我们会覆盖setUp

标签: python unit-testing


【解决方案1】:

这是因为您错误地覆盖了__init__()。几乎可以肯定,您根本不想覆盖__init__();你应该在setUp() 中做所有事情。我已经使用 unittest 超过 10 年了,我认为我从未覆盖过 __init__()

但是,如果您确实需要覆盖__init__(),请记住,您无法控制调用构造函数的位置——框架会为您调用它。所以你必须提供一个它可以调用的签名。从源代码(unittest/case.py)来看,签名是:

def __init__(self, methodName='runTest'):

这样做的安全方法是接受任何参数并将它们传递给基类。这是一个有效的实现:

class BaseTest(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        unittest.TestCase.__init__(self, *args, **kwargs)

    def setUp(self):
        print "Base.setUp()"

    def tearDown(self):
        print "Base.tearDown()"


class TestSomething(BaseTest):
    def __init__(self, *args, **kwargs):
        BaseTest.__init__(self, *args, **kwargs)
        self.controller = object()

    def test_silly(self):
        self.assertTrue(1+1 == 2)

【讨论】:

  • 我很好奇...为什么不使用init?在我的情况下,我将 Django 的 TestCase 子类化为通用类,然后将 that 子类化以创建特定的测试。测试之间的唯一区别在于类属性,这是放在__init__() 中的完美东西。我在这里错过了什么?
  • @mlissner:首先,您的术语有误。如果你在__init__() 中设置一个属性,它不是一个类属性——它是一个实例属性。当你在类范围内设置一些东西时,在任何方法之外,那么它是一个类属性。
  • @mlissner:其次,覆盖TestCase.__init__() 非常罕见,因为它根本没有必要。对于每个测试,unittest 创建一个新的测试类实例(调用__init__()),调用setUp(),调用正在运行的test*() 方法,然后调用tearDown()setUp()是官方指定的扩展点,每次调用__init__()setUp()也是。
  • 感谢@GregWard。所以,一句话:约定。 Unittest 是一个奇怪的工具,按照惯例,它希望在setUp 中使用这种东西。所以现在这是一个单元测试的约定与 Python 的问题,我想。也感谢您的术语更正。
【解决方案2】:

BaseController__init__ 中,您需要像在TestMyController 中那样调用unittest.TestCase__init__

从框架构造 TestCase 的调用可能会传递一个参数。处理派生类的最佳方法是:

class my_subclass(parentclass):
    def __init__(self, *args, **kw):
        parentclass.__init__(self, *args, **kw)
        ...

【讨论】:

    猜你喜欢
    • 2017-05-04
    • 2019-03-26
    • 2012-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多