Единичное и функциональное тестирование приложения на основе PySide?
Я создаю приложение на основе PySide 1.1.0 и ищу хорошие примеры, чтобы посмотреть на модульное и функциональное тестирование моего приложения. Я хочу иметь возможность выполнять функциональное тестирование пользовательского интерфейса (имитирующие клики, нажатия клавиш и т.д.), Модульное тестирование слотов пользовательского интерфейса, которые изменяют компоновку пользовательского интерфейса (предположительно, с использованием частично изделенного отправителя и получателя), а также блок тестирование кода, который включает в себя виджеты, но не требующие визуализации окон.
В качестве одного примера я динамически создаю подменю одного меню в строке меню, когда элемент добавляется к модели (объект, созданный QAbstractItemModel), который предоставляет данные в QTreeView. Модель и подменю должны оставаться в синхронизации, поэтому я хочу написать unit test, который передает данные контроллеру, который управляет моделью и подменю, и утверждает, что и модель, и подменю были надлежащим образом обновлены.
Я бы предпочел НЕ настраивать QApplication в моем тестовом коде, если я могу его избежать. Я также хотел бы не показывать какие-либо окна, когда мне нужно только проверять структуры данных в виджетах, а не их визуализацию.
Я не могу найти что-либо подходящее значение в http://www.pyside.org или в моих поисковых системах Google. Есть ли у кого-нибудь опыт или хороший код примера, на который я должен смотреть?
Ответы
Ответ 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: должен построить QApplication перед QPaintDevice".
Если вам нужен экземпляр 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)
надеюсь, что это поможет