在python unittest中定义一次性类

时间:2014-06-15 08:23:58

标签: python python-unittest

如果这是重复的话,请随意关闭,但我还没有看到类似的东西。我来自更多的“经典OOP”背景(例如Java,C#,C ++,PHP(如果你想称之为OOP语言......)等),我现在正试图掌握Python。 / p>

假设我们有一个这样定义的类:

class Foo:

  def __init__(self):
    self.stuff = self.defineStuff()

  def method1(self, x):
    return x

  def defineStuff(self):
    # this method has no logic in this class; child classes should define this
    pass

  def doStuff(self):
    return self.stuff

class FooTest(unittest.TestCase):
  def testMethod1(self):
    assertEquals(5, Foo().method1(5))
好的,到目前为止一切顺利。但是,我真正想要测试的是doStuff正常工作;上面的实现对于它自己的好处来说可能太微不足道了但是我没有在这里发布我的实际代码。由于doStuff依赖于defineStuff来填充stuff成员变量,因此测试Foo.doStuff()是没有意义的,除非我想测试基类实现是否抛出AttributeError。 (这是有效的,但不是我想要测试的)。但是,我可以这样做:

class FooTest(unittest.TestCase):
  # we did this before, it's just here for reference
  def testMethod1(self):
    assertEquals(5, Foo().method1(5))

  def testDefineStuff(self):
    class Bar(Foo):
      def defineStuff(stuff):
        self.stuff = stuff
        return self
    bar = Bar().defineStuff('abcdefg')
    self.assertEquals('abcdefg', bar.stuff)

我尝试在setUp()方法中定义Bar类,并在FooTest类中作为一个单独的方法,在我尝试使用它之前都没有定义类;我得到一个NameError异常。我是否被迫在每种测试方法中定义Bar类,或者是否存在我缺少的东西? Bar类应该是一个测试夹具;我不打算在测试之外使用它,但它确实展示了Foo父类的具体功能。我的例子是做作和过度简化的,但我的观点是,我不想为Bar类定义一个单独的类文件,因为它仅用于测试。

如果我在FooTest类定义中定义了Bar类,这会有用吗? Python是否具有Java中的内部类?

2 个答案:

答案 0 :(得分:1)

这让我想起了PHPUnit的模拟对象。我相信unittest模块不支持它们,但也许有一些插件可以为你提供这个功能,这肯定比推出你自己的解决方案更好,我会先尝试找到这样一个插件。

无论如何,Python非常通用,特别是它的类也是对象!因此,您可以这样做:

class FooTest(unittest.TestCase):
    def setUp(self):
        class Bar(Foo):
            def defineStuff(stuff):
                self.stuff = stuff
                return self
        # store class locally
        self.Bar = Bar

    def testDefineStuff(self):
        bar = self.Bar().defineStuff('abcdefg')
        self.assertEquals('abcdefg', bar.stuff)

关于嵌套类的问题,我不知道,只是试一试。请记住,其他几种语言的隐式范围(即this是可选的)不是Python的特性/错误之一,因此您始终必须将嵌套类称为self.Bar或{{ 1}}。

答案 1 :(得分:0)

我昨晚解释了我试图做的相当糟糕的事情(在凌晨4点发布问题可能不是最聪明的想法)。鉴于我的例子出现的方式,评论是有效的。

下面的Foo类比我原来的例子有点小:

class Foo:
  def __init__(self):
    self.stuff = self.defineStuff()

  def defineStuff(self):
    return []

  def doStuff(self):
    try:
      return [x + x for x in self.stuff]
    except TypeError:
      raise FooException("defineStuff must return a list")

class FooTest(unittest.TestCase):
  def testDoStuff(self):
    v = Foo().doStuff()
    self.assertEquals([], v)

所以,这里defineStuff方法不是存根,而是返回它可以使用的最基本值,因为这个类是要扩展的。现在,测试doStuff甚至defineStuff实际上是有意义的,因为这两种方法都已实际实现。

回到我试图用我原来的问题提出的问题......正如我在评论中提到的,我不是打算来定义这个项目范围内的子类。 (我确实打算在其他项目中这样做,但我想知道这个项目的工作方式正如我所期望的那样,然后才转向那些。)因此,即使实际使用默认值定义defineStuff,我仍然不知道更复杂的情况是否有效。

如果子类将defineStuff定义为返回[[1,2], [3,4], [5,6]]怎么办?我知道这会正常工作并返回[[1, 2, 1, 2], [3, 4, 3, 4], [5, 6, 5, 6]]吗?从理论上讲,它应该。这个例子特别简单,我可以看一下,知道它会。但是我没有对它进行过一定的测试,我不想定义一个单独的子类,因为我永远不会使用它。

关于你是否可以在unittest.TestCase派生类的setUp方法中定义一个类,并且你的测试方法可以识别它,但是我猜不会。但是,我意识到我可以按照以下方式做事:

class FooTest(unittest.TestCase):
  @staticmethod
  def sampleStuff(placeholder):
    return [[1, 2], [3, 4], [5, 6]]

  def testDoStuff(self):
    Foo.defineStuff = FooTest.sampleStuff
    v = Foo().doStuff()
    self.assertEquals([[1, 2, 1, 2], [3, 4, 3, 4], [5, 6, 5, 6]], v)

因此,这样,我不需要为测试定义一次性测试类,但我至少可以覆盖客户端定义defineStuff的可能方式的一部分。我无法涵盖所有​​内容,而且我不打算这样做,但是我可以做的测试案例越多,我就越能确保我的逻辑按预期工作。