【问题标题】:how should I test method that does not return anything?我应该如何测试不返回任何内容的方法?
【发布时间】:2013-11-23 13:47:59
【问题描述】:

这是我最近制作的工具的代码,我正在尝试编写单元测试,我有一个 idea of how to test a method that returns something 但不明白我应该如何测试不喜欢下面的方法:

def buildUi(self):
    self.label = QtGui.QLabel()
    self.label.setAlignment(QtCore.Qt.AlignCenter)
    self.setCentralWidget(self.label)

def nextImage(self):
    """ switch to next image or previous image
    """
    if self._imagesInList:
        if self._count == len(self._imagesInList):
            self._count = 0

        self.showImageByPath(
                self._imagesInList[self._count])

        if self.animFlag:
            self._count += 1
        else:
            self._count -= 1


def showImageByPath(self, path):
    if path:
        image = QtGui.QImage(path)
        pp = QtGui.QPixmap.fromImage(image)
        self.label.setPixmap(pp.scaled(
                self.label.size(),
                QtCore.Qt.KeepAspectRatio,
                QtCore.Qt.SmoothTransformation))

def playPause(self):
    if not self._pause:
        self._pause = True
        self.updateTimer.start(2500)
        return self._pause
    else:
        self._pause = False
        self.updateTimer.stop()

def keyPressEvent(self, keyevent):
    """ Capture key to exit, next image, previous image,
        on Escape , Key Right and key left respectively.
    """
    event = keyevent.key()
    if event == QtCore.Qt.Key_Escape:
        self.close()
    if event == QtCore.Qt.Key_Left:
        self.animFlag = False
        self.nextImage()
    if event == QtCore.Qt.Key_Right:
        self.animFlag = True
        self.nextImage()
    if event == 32:
        self._pause = self.playPause()

查看的完整代码可以在here找到

是否可以测试上面的这些方法,或者我必须修改以使它们可测试? 编辑:更新:

class TestSlideShow(unittest.TestCase):
    """ docstring for TestSlideShow
    """
    def setUp(self):
        self.mox = mox.Mox()
        self.imgLst = ['/folder/test/images/test1.jpg', '/folder/test/images/test2.JPG',
        '/folder/test/images/test3.png', '/folder/test/images/test4.PNG']
        app = QtGui.QApplication([])
        self.show = slideShow.SlideShowPics(imgLst=self.imgLst, ui=False)

def tearDown(self):
    self.mox.UnsetStubs()
    self.mox.ResetAll()

def test_nextImage(self):
    self.mox.StubOutWithMock(self.show, 'prepairWindow')
    self.show.prepairWindow()
    self.mox.StubOutWithMock(self.show, 'showImageByPath')
    self.show.showImageByPath(self.imgLst[1])
    self.show.nextImage()
    # self.mox.ReplayAll()
    self.assertEquals(1, self.show.count)
    self.assertEquals(self.imgLst[1], self.show._imagesInList[1])
    # self.mox.VerifyAll()

def test_nextImage_animFlag_False(self):
    self.show.animFlag = False
    self.show.count = 4

    self.mox.StubOutWithMock(self.show, 'prepairWindow')
    self.show.prepairWindow()
    self.mox.StubOutWithMock(self.show, 'showImageByPath')
    self.show.showImageByPath(self.imgLst[3])
    print self.show.count
    self.show.nextImage()
    print self.show.count       
    # self.assertEquals(3, self.show.count)
    self.assertEquals(self.imgLst[3], self.show._imagesInList[3])


if __name__ == '__main__':
    unittest.main()

self.show.animFlag 为 True 时的第一个测试工作正常,但是当我手动设置 animFlag= False 时,第二个测试失败。

【问题讨论】:

  • 虽然函数没有返回任何东西,但它们实际上做了一些事情; ) 这就是您要测试的内容。您创建一个对象(例如模拟对象),调用影响该对象的函数,然后检查它并检查一切是否按预期进行。
  • 看看模拟和存根voidspace.org.uk/python/mock
  • @User : 我想用 mox 来做。
  • @septi 你能举个例子吗?

标签: python unit-testing testing ui-testing


【解决方案1】:

这是在代码之后编写 unittest 的问题 - 然后您会意识到您的代码很难测试。在代码之前编写测试(嗯,真的是“沿着”代码 - 你不会在开始编码之前编写 所有 测试,但你仍然不会在测试之前编写一行代码) 确保您没有这样的问题。

现在,即使使用“测试优先”方法,您也必须测试不返回任何内容的方法。这样做的方法是测试预期的副作用。其中一些副作用很容易测试 - 在上述情况下,您可以在调用 nextImage 之前和之后测试 self._count 的值,具体取决于对象的状态(主要是 _imagesInListanimFlag)。如果您想测试nextImage 是否确实使用正确的参数调用showImageByPath,并且您当前的设计唯一的方法是为测试使用monkeypatch showImageByPath,则变得更加困难。测试showImageByPath 将需要修补/模拟self.label.setPixmap() 等。

正如其他人已经指出的那样,有几个模拟/存根库可以提供帮助,但它们不会解决所有可能的可测试性问题,您可能需要重新考虑您的设计以使事情变得更容易 - 比如不硬编码对 @987654330 的调用@ in buildUI() 但有一些方法可以“注入”所需的组件(QtGui.QLabel() 或模拟)。作为一般规则,可测试的代码几乎没有硬编码的依赖关系,很少有副作用,并且有很多带有短方法的小类(而不是带有长方法的大类)。

【讨论】:

  • 我可以在 nextImage 中模拟 showImageByPath,但是我不明白您的意思是不在 buildUI() 中硬编码对 QtGui.QLabel() 的调用以及术语副作用在测试中指的是什么?
  • @san 函数的副作用是它以不同于返回值的方式改变程序状态的方式。例如,def f(): global x; x += 1; return x * 5 会产生将 1 加到 x 的副作用。在 Python 中通常最好避免使用它们。
  • @san: wrt/ 副作用,我主要是想你的方法确实改变了实例的状态或构建对象或调用其他对象的方法等。如果一个方法应该改变你的对象的以给定的方式状态,您可以在方法调用后测试对象的状态是否是您所期望的。
  • @san: wrt/ harcoding,简单明了:组件名称QtGui.QLabelbuildUI中用普通字母写成,所以你不能让buildUI实例化另一个组件——好吧,这是 Python,因此您实际上仍然可以对整个环境进行猴子补丁以使 QtGui.QLabel 指向其他内容,但是如果您将类传递给您将提供预期组件的其他“UIBuilder”对象并使用任一QtUIBuilder 用于生产或一些MockUIBuilder 用于测试。这称为“依赖注入”。
猜你喜欢
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
  • 2021-11-22
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 2019-10-23
  • 2020-01-26
相关资源
最近更新 更多