【问题标题】:Unit and functional testing a PySide-based application?单元和功能测试基于 PySide 的应用程序?
【发布时间】:2012-06-21 19:41:54
【问题描述】:

我正在构建一个基于 PySide 1.1.0 的应用程序,并且一直在寻找好的示例来查看我的应用程序的单元和功能测试。我希望能够对 UI 进行功能测试(模拟点击、按键等),对改变 UI 布局的 UI 插槽进行单元测试(可能使用部分模拟的发送器和接收器),以及单元测试涉及小部件的代码,但不需要渲染任何窗口。

作为一个例子,当一个项目被添加到向 QTreeView 提供数据的模型(QAbstractItemModel 派生对象)时,我在菜单栏中动态创建一个菜单的子菜单。模型和子菜单必须保持同步,因此我希望能够编写一个单元测试,将数据提交给管理模型和子菜单的控制器,并断言模型和子菜单都已正确更新。

如果可以避免的话,我宁愿不必在我的测试代码中设置 QApplication。当我只关心验证小部件中的数据结构而不是它们的可视化时,我还希望不必显示任何窗口。

我在http://www.pyside.org 或我的 Google 搜索中找不到任何合适的价值。有没有人有任何经验或知道我应该看的好的示例代码?

【问题讨论】:

标签: python unit-testing qt4 pyside


【解决方案1】:

我现在一直在玩一些单元测试 pyside 代码,并得出结论,将 python 的 unittest 模块与 qt 的 QTest 模块结合起来效果很好。

您必须实例化一个QApplication 对象,但您不需要运行它的exec_ 方法,因为您不需要运行事件循环。

下面是一个示例,说明我如何测试对话框中的 QCheckBox 是否符合预期:

class Test_PwsAddEntryDialog(TestCase):
    """Tests the class PwsAddEntryDialog."""

    def test_password_strength_checking_works(self):
        """Tests if password strength checking works, if the corresponding check
        box is checked.
        """
        d = PwsAddEntryDialog()
        # test default of internal flag
        self.assertFalse(d.testPasswordStrength)
        # type something
        QTest.keyClicks(d.editSecret, "weak", 0, 10)
        # make sure that entered text is not treated as a password
        self.assertEqual(d.labelPasswordStrength.text(), "")
        # click 'is password' checkbox
        QTest.mouseClick(d.checkIsPassword, Qt.LeftButton)
        # test internal flag changed
        self.assertTrue(d.testPasswordStrength)
        # test that label now contains a warning
        self.assertTrue(d.labelPasswordStrength.text().find("too short") > 0)
        # click checkbox again
        QTest.mouseClick(d.checkIsPassword, Qt.LeftButton)
        # check that internal flag once again changed
        self.assertFalse(d.testPasswordStrength)
        # make sure warning disappeared again
        self.assertEqual(d.labelPasswordStrength.text(), "")

这完全可以在屏幕外工作,包括点击小部件并在 QLineEdit 中输入文本。

这是我测试(相当简单的)QAbstractListModel 的方法:

class Test_SectionListModel(TestCase):
    """Tests the class SectionListModel."""

    def test_model_works_as_expected(self):
        """Tests if the expected rows are generated from a sample pws file
        content.
        """
        model = SectionListModel(SAMPLE_PASSWORDS_DICT)
        l = len(SAMPLE_PASSWORDS_DICT)
        self.assertEqual(model.rowCount(None), l)
        i = 0
        for section in SAMPLE_PASSWORDS_DICT.iterkeys():
            self.assertEqual(model.data(model.index(i)), section)
            i += 1

我希望这会有所帮助。

【讨论】:

    【解决方案2】:

    在我的情况下,我收到错误“QPixmap:必须在 QPaintDevice 之前构造 QApplication”。

    如果您需要有一个 QApplication 实例用于您的测试(例如使用 QPixmap),这是一种方法。只需创建一个单例,以确保您只有一个 QApplication 实例。

    这是作为 PySide 源代码中测试的助手而隐藏的。

    import unittest
    
    from PySide.QtGui import QApplication
    _instance = None
    
    class UsesQApplication(unittest.TestCase):
        '''Helper class to provide QApplication instances'''
    
        qapplication = True
    
        def setUp(self):
            '''Creates the QApplication instance'''
    
            # Simple way of making instance a singleton
            super(UsesQApplication, self).setUp()
            global _instance
            if _instance is None:
                _instance = QApplication([])
    
            self.app = _instance
    
        def tearDown(self):
            '''Deletes the reference owned by self'''
            del self.app
            super(UsesQApplication, self).tearDown()
    

    然后是UsesQApplication的子类

    from PySide import QtGui
    
    class Test(UsesQApplication):
    
        def setUp(self):
            #If you override setup, tearDown, make sure
            #to have a super call
            super(TestFilterListItem, self).setUp()
    
        def tearDown(self):
            super(TestFilterListItem, self).tearDown()
    
        def testName(self):
            pix = QtGui.QPixmap(20,20)
            self.assertTrue(True)
    

    希望对你有帮助

    【讨论】:

    • 我只是在每个使用 QtGui 的测试模块的开头做if QtGui.qApp == None: QtGui.QApplication([])
    猜你喜欢
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多