Question

This is the code from my recent tool I have made, I am trying to write unittest, I have an idea of how to test a method that returns something but don't understand how should i test method that don't like below:

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()

the complete code for looking can be found here

is it possible to test these methods above or do I have to modify to make them testable ? Edit: updated:

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()

the first test when self.show.animFlag is True works fine and but the when I manually set the animFlag= False then second test fails.

Was it helpful?

Solution

This is the problem with writing unittest after the code - you then realize your code is difficult to test. Writing the tests before the code (well, really "along" the code - you don't write all tests before start coding, but still you dont write a line of code before you have a test for it) makes sure you don't have such a problem.

Now even with the "test first" approach you do have to test methods that don't return anything. The way to do so is to test for the expected side effects. Some of these side effects are easy to test - in your above case, you can test the value of self._count before and after the call to nextImage, depending on your object's state (_imagesInList and animFlag mostly). Where it gets more difficult is if you want to test that nextImage does actually call showImageByPath with the correct arguments, and with your current design the only way to do so is to monkeypatch showImageByPath for the tests. Testing showImageByPath will require patching / mocking self.label.setPixmap(), etc.

As others already pointed there are a couple mock/stub libs that can help, but they won't solve all possible testability issues and you may have to rethink your design to make things easier - like not hardcoding the call to QtGui.QLabel() in buildUI() but have some way to "inject" the desired componant (QtGui.QLabel() or a mock) instead. As a general rule, testable code has very few hard-coded dependencies, few side effects, and lot of small classes with short methods (instead of huge classes with long methods).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top