我正在为PyQt5 MainWindow GUI编写功能测试。目前我正在检查正确的小部件是否在网格布局的正确坐标中。我已经确定了如何通过其objectName找到一个小部件,并找出了在GUI的(网格)布局中“挖掘”到小部件所需的模块层次结构,但我觉得我必须这么做。我一直在阅读PyQt4 / 5文档并在SO上搜索类似的问题,但没有找到可以简化我的任务的工具。
理想情况下,我希望命令类似于以下伪代码中的命令:
Bob_widget = widget['Bob'] # lookup widget by objectName
assert Bob_widget.layout.coordinates == (0, 0)
伪代码的第一行的功能可以通过将每个小部件名称和类型传递到findChild方法然后制作字典来完成,但我想知道是否有内置的等价物。
伪代码的第二行更像是一种痛苦。如何通过GUI浏览以找到小部件进行了一些研究。对于我的GUI代码:
import sys
from collections import namedtuple
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
QLabel, QDoubleSpinBox)
gui_userinput = namedtuple('gui_userinput', ['key', 'string', 'value', 'range'])
variable_a = gui_userinput(key='variable_a', string='Variable A', value=165.00,
range=(0.00, 10000.00))
variable_b = gui_userinput(key='variable_b', string='Variable B', value=135.00,
range=(0.00, 10000.00))
gui_userinput_order = (variable_a, variable_b)
class Gui(QMainWindow):
def __init__(self, parent=None):
super(Gui, self).__init__(parent)
self.setObjectName('toplevel')
self.setupUi()
def setupUi(self):
centralWidget = QWidget()
centralWidget.setObjectName('centralwidget')
self.setCentralWidget(centralWidget)
centralLayout = QGridLayout()
self.setObjectName('centrallayout')
for i, widget in enumerate(gui_userinput_order):
wlabel = QLabel(widget.string)
wlabel.setObjectName(widget.key + '_label')
wbox = QDoubleSpinBox()
wbox.setObjectName(widget.key)
wbox.setRange(*widget.range)
wbox.setValue(widget.value)
centralLayout.addWidget(wlabel, 0, i)
centralLayout.addWidget(wbox, 1, i)
centralWidget.setLayout(centralLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = Gui()
ui.show()
sys.exit(app.exec_())
从MainWindow获取一个小部件需要一个如下语句:
widget_0_0 = self.ui.centralWidget().layout()\
.itemAtPosition(0, 0).widget()
“GUI有一个中央小部件,它有一个(网格)布局,在网格坐标(0,0)上有一个小部件容器,用于保存我想要的小部件。”
所以我可以编写下面最小例子的测试代码:
import sys
from PyQt5.QtWidgets import QApplication, QDoubleSpinBox, QLabel
import mini_ui
app = QApplication(sys.argv)
class TestMainGUi:
def setup(self):
self.ui = mini_ui.Gui()
def test_all_widgets_exist(self):
"""The user finds the expected labels and doublespinboxes."""
for widget in mini_ui.gui_userinput_order:
found_box = self.ui.findChild(QDoubleSpinBox, widget.key)
assert found_box.value() == widget.value
found_label = self.ui.findChild(QLabel, widget.key + '_label')
assert found_label.text() == widget.string
def test_label_0_0(self):
"""The user sees a label for 'Variable A' in the top left corner."""
label_a = self.ui.findChild(QLabel, 'variable_a_label')
print('Found', label_a, 'named', label_a.objectName(), 'in GUI.')
widget_0_0 = self.ui.centralWidget().layout()\
.itemAtPosition(0, 0).widget()
print('Found widget type', type(widget_0_0), 'at grid 0, 0')
print('It is named: ', widget_0_0.objectName())
assert widget_0_0.text() == 'Variable A'
assert widget_0_0 is label_a
def test_alternate_widget_acces(self):
"""Just hacking away trying to find a more terse widget reference"""
label_a = self.ui.findChild(QLabel, 'variable_a_label')
assert 1 == 1
def teardown(self):
pass
并更改第二个测试以迭代匹配组件小部件到网格坐标小部件,但这感觉不对。除了令人费解,如果有任何关于GUI的更改以改变层次结构,测试将无法工作 - 测试代码必须知道GUI的精细结构。
是否有更简洁,更健壮的方法来测试GUI布局的内容?是否有我缺少的内置工具?
感谢任何指导。
答案 0 :(得分:0)
我有一个部分解决方案,用于按名称查找布局,这意味着测试现在可能不知道确切的GUI结构。
如果在使用centralWidget.setLayout()
将其应用于centralWidget之前将名称指定给布局,则布局名称丢失。如果您设置了centralWidget的布局,并且然后为centralWidget.layout()
指定了名称,则可以按名称找到布局。
所以,修改mini_ui为:
centralWidget.setLayout(centralLayout)
centralWidget.layout().setObjectName('centrallayout')
然后功能测试可以找到布局并将其分配给变量:
def test_alternate_widget_access(self):
"""Just hacking away trying to find a more terse widget reference"""
main_layout = self.ui.findChild(QGridLayout, 'centrallayout')
found_widget = main_layout.itemAtPosition(0, 0).widget()