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