[英]travis with pytest-qt fails without exiting nor creating QmessageBox
我嘗試為使用Qt5
通過python3
編寫的圖形用戶界面(GUI)創建集成測試(因此使用pyqt5
)。 我使用pytest
和插件pytest-qt
來測試 GUI。
我測試了很大程度上受此問題啟發的 GUI,因此命令pytest -v -s
運行良好。
由於我的存儲庫在Github
,我使用Travis-CI
來執行我的集成測試。
但是,當我推動Github
並啟動Travis
測試時,我在某些時候遇到以下錯誤:
Exceptions caught in Qt event loop:
________________________________________________________________________________
Traceback (most recent call last):
File "/home/travis/build/XXXX/Test/GUI_test.py", line 29, in handle_dialog
yes_button = messagebox.button(QtWidgets.QMessageBox.Yes)
AttributeError: 'Example' object has no attribute 'button'
我使用包含在我的 git 存儲庫中的以下文件在 MWE 中重現此錯誤:
用 python GUI.py
編寫的 GUI:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QCoreApplication, Qt, QObject
from PyQt5.QtGui import QIcon
class Example(QMainWindow):
def __init__(self, parent = None):
super().__init__()
self.initUI(self)
def initUI(self, MainWindow):
# centralwidget
MainWindow.resize(346, 193)
self.centralwidget = QtWidgets.QWidget(MainWindow)
# The Action to quit
self.toolb_action_Exit = QAction(QIcon('exit.png'), 'Exit', self)
self.toolb_action_Exit.setShortcut('Ctrl+Q')
self.toolb_action_Exit.triggered.connect(self.close)
# The Button
self.btn_prt = QtWidgets.QPushButton(self.centralwidget)
self.btn_prt.setGeometry(QtCore.QRect(120, 20, 89, 25))
self.btn_prt.clicked.connect(lambda: self.doPrint() )
self.btn_quit = QtWidgets.QPushButton(self.centralwidget)
self.btn_quit.setGeometry(QtCore.QRect(220, 20, 89, 25))
self.btn_quit.clicked.connect(lambda: self.close() )
# The textEdit
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(10, 60, 321, 81))
# Show the frame
MainWindow.setCentralWidget(self.centralwidget)
self.show()
def doPrint(self):
print('TEST doPrint')
def closeEvent(self, event):
# Ask a question before to quit.
self.replyClosing = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if self.replyClosing == QMessageBox.Yes:
event.accept()
else:
event.ignore()
def main_GUI():
print('start')
app = QApplication(sys.argv)
imageViewer = Example()
return app, imageViewer
if __name__ == '__main__':
app, imageViewer =main_GUI()
rc= app.exec_()
print('App end is exit code {}'.format(rc))
sys.exit(rc)
pytest
用來創建 unittest GUI_test.py
的文件:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, sys
from PyQt5 import QtGui, QtCore, QtWidgets, QtTest
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QCoreApplication, Qt, QObject
import pytest
import warnings
from pytestqt.plugin import QtBot, capture_exceptions
import mock
@pytest.fixture(scope="module")
def Viewer(request):
print(" SETUP GUI")
GUI= __import__('GUI')
app, imageViewer = GUI.main_GUI()
with capture_exceptions() as exceptions:
qtbotbis = QtBot(app)
QtTest.QTest.qWait(0.5 *1000)
yield app, imageViewer, qtbotbis
######### EXIT ##########
app.quitOnLastWindowClosed()
def handle_dialog():
messagebox = QtWidgets.QApplication.activeWindow()
yes_button = messagebox.button(QtWidgets.QMessageBox.Yes)
qtbotbis.mouseClick(yes_button, QtCore.Qt.LeftButton, delay=1)
QtCore.QTimer.singleShot(100, handle_dialog)
qtbotbis.mouseClick(imageViewer.btn_quit, QtCore.Qt.LeftButton, delay=1)
assert imageViewer.isHidden()
app.closeAllWindows()
app.quit()
app.exit()
app.closingDown()
QtTest.QTest.qWait(0.5 *1000)
with mock.patch.object(QApplication, "exit"):
app.exit()
assert QApplication.exit.call_count == 1
print("[Notice] So a mock.patch is used to count if the signal is emitted.")
print(" TEARDOWN GUI")
class Test_GUI_CXS() :
def test_buttons(self, Viewer, caplog):
app, mainWindow, qtbot = Viewer
qtbot.mouseClick( mainWindow.btn_prt, QtCore.Qt.LeftButton )
控制 travis 作業.travis.yml
的文件(它能夠根據文檔p32 處理圖形窗口):
language: python
python:
- "3.7"
sudo: required
dist: bionic
jobs:
include:
- stage: test
name: PyTest-GUI
before_install:
- python -m pip install --upgrade pip
- pip install -r ./requirement.txt
- sudo apt-get install -y libdbus-1-3 libxkbcommon-x11-0 dzen2
install:
- "export DISPLAY=:99.0"
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac+extension GLX +render -noreset"
- sleep 3
before_script:
- "herbstluftwm &"
- sleep 1
script:
- pytest -s -v ./GUI_test.py
addons:
apt:
packages:
- x11-utils
- libxkbcommon-x11-0
- herbstluftwm
- xvfb
services: xvfb
以及包含所需庫的文件requirement.txt
:
pyqt5
mock
pytest
pytest-qt
我嘗試在調試模式下運行 travis 作業。 因此,在通過ssh
連接安裝所有依賴項后,我嘗試運行命令pytest
並得到相同的錯誤。
但是,如果我做herbstluftwm &
然后pytest
測試運行良好,沒有出現錯誤。 因此,我假設在正常的 travis 作業中命令herbstluftwm &
存在問題,但我不知道如何解決它。
歡迎任何提示或幫助!
在我之前的答案中,我憑經驗選擇了 100 毫秒,但根據資源,時間可能會有所不同,因此我不必放置可能失敗的時間,並實現了每 T 秒運行一次的函數,直到找到 QMessageBox。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtWidgets, QtTest
import mock
import pytest
from pytestqt.plugin import QtBot, capture_exceptions
def get_messagebox(t=100, max_attemps=-1):
messagebox = None
attempt = 0
loop = QtCore.QEventLoop()
def on_timeout():
nonlocal attempt, messagebox
attempt += 1
active_window = QtWidgets.QApplication.activeWindow()
if isinstance(active_window, QtWidgets.QMessageBox):
messagebox = active_window
loop.quit()
elif max_attemps > 0:
if attempt > max_attemps:
loop.quit()
else:
QtCore.QTimer.singleShot(t, on_timeout)
QtCore.QTimer.singleShot(t, on_timeout)
loop.exec_()
return messagebox
@pytest.fixture(scope="module")
def Viewer(request):
print(" SETUP GUI")
GUI = __import__("GUI")
app, imageViewer = GUI.main_GUI()
with capture_exceptions():
qtbotbis = QtBot(app)
QtTest.QTest.qWait(0.5 * 1000)
yield app, imageViewer, qtbotbis
app.quitOnLastWindowClosed()
def handle_dialog():
messagebox = get_messagebox()
yes_button = messagebox.button(QtWidgets.QMessageBox.Yes)
qtbotbis.mouseClick(yes_button, QtCore.Qt.LeftButton, delay=1)
QtCore.QTimer.singleShot(10, handle_dialog)
qtbotbis.mouseClick(imageViewer.btn_quit, QtCore.Qt.LeftButton, delay=1)
assert imageViewer.isHidden()
app.closeAllWindows()
app.quit()
app.exit()
app.closingDown()
QtTest.QTest.qWait(0.5 * 1000)
with mock.patch.object(QtWidgets.QApplication, "exit"):
app.exit()
assert QtWidgets.QApplication.exit.call_count == 1
print("[Notice] So a mock.patch is used to count if the signal is emitted.")
print(" TEARDOWN GUI")
class Test_GUI_CXS:
def test_buttons(self, Viewer, caplog):
app, mainWindow, qtbot = Viewer
qtbot.mouseClick(mainWindow.btn_prt, QtCore.Qt.LeftButton)
另一方面,要在 travis 中進行測試,只需要以下配置:
language: python
python:
- "3.7"
dist: bionic
jobs:
include:
- stage: test
name: PyTest-GUI
before_script:
- python -m pip install --upgrade pip
- pip install -r ./requirement.txt
script:
- pytest -s -v ./GUI_test.py
addons:
apt:
packages:
- libxkbcommon-x11-0
services:
- xvfb
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.