简体   繁体   中英

Asserting `mocked_function.called` for unittest

I am looking to implement unittests for PySide UI and can't figure out how to properly assert that the functions connected to ui elements got called.

So far I've tried different ways to import the module I am writing the test for. Different ways of mocking the function

../app/ui.py

import sys

from PySide.QtGui import *
from PySide.QtCore import *

def btn1_callback():
    print("1")

class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)

        layout = QVBoxLayout()

        self.btn1 = QPushButton("Foo")
        self.btn1.clicked.connect(btn1_callback)

        layout.addWidget(self.btn1)
        self.setLayout(layout)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    app.exec_()

../tests/test_ui.py

import sys
import unittest
import mock

from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtTest import QTest

import app.ui

application = QApplication(sys.argv)

class UiTest(unittest.TestCase):

    def setUp(self):
        self.ui = app.ui.Window()

    @mock.patch.object(app.ui, "btn1_callback")
    def test_btn1(self, callback):
        QTest.mouseClick(self.ui.btn1, Qt.LeftButton)
        self.assertTrue(callback.called)

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

Running python -m unittest discover tests will run the 1 test and fail saying that the function was not called. It will however have printed "1" to the console so I know the QTest did manage to cause the callback to be executed.

patch.object basically modifies what access to the name app.ui.btn1_callback resolves to. However, you aren't calling it until after app.ui.Window() has already gotten its own reference to that function. That reference isn't affected by the patch. You have to instantiate the UI and run the test while the patch is still in effect:

class UiTest(unittest.TestCase):
    def setUp(self):
        p = mock.patch.object(app.ui, "btn1_callback")
        self.addCleanup(p.stop)
        self.callback = p.start()
        self.ui = app.ui.Window()

    def test_btn1(self):
        QTest.mouseClick(self.ui.btn1, Qt.LeftButton)
        self.assert(self.callback.called)

Now startup is responsible for enabling the patch, as well as scheduling its eventual disabling. Window() and the test itself run in an environment where the callback has already been patched.

(Using addCleanup is safer than defining tearDown to call p.stop() because it ensures the patch is stopped even if tearDown isn't called for any reason.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM