使用 pytest 测试 pyqt 应用程序

testing pyqt application with pytest

我正在使用 pytest 及其 pytest-qt 插件测试 PyQt5 应用程序。

我创建了一个夹具来加载我的应用程序 class 然后我将其用于所有测试

到目前为止我有两个问题

  1. gui出现了,我不要了。
  2. 如果我编写更多测试,我最终会遇到分段错误,因为(我认为)打开的 gui 太多。

有什么想法吗? pytest-qt 的文档非常基础,实际上有一个显示 GUI

的测试

一个最小的例子是:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QCoreApplication, QObject, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import *


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():
    app = QApplication(sys.argv)
    imageViewer = Example()
    imageViewer.show()
    return app, imageViewer


if __name__ == "__main__":
    app, imageViewer = main_GUI()
    rc = app.exec_()
    print("App end is exit code {}".format(rc))
    sys.exit(rc)

测试:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys

import pytest
from PyQt5 import QtCore, QtGui, QtTest, QtWidgets
from PyQt5.QtCore import QCoreApplication, QObject, Qt
from PyQt5.QtWidgets import *
from pytestqt.plugin import QtBot

GUI = __import__("GUI")


@pytest.fixture(scope="module")
def qtbot_session(qapp, request):
    print("  SETUP qtbot")
    result = QtBot(qapp)
    with capture_exceptions() as exceptions:
        yield result
    print("  TEARDOWN qtbot")


@pytest.fixture(scope="module")
def Viewer(request):
    print("  SETUP GUI")

    app, imageViewer = GUI.main_GUI()
    qtbotbis = QtBot(app)
    QtTest.QTest.qWait(0.5 * 1000)

    return  app, imageViewer, qtbotbis




def test_interface(Viewer):
    print("  beginning ")
    app, imageViewer, qtbot = Viewer

    assert imageViewer.textEdit.toPlainText() == ''

如果您不想显示 GUI,那么一个可能的解决方案是使用 Qt::WA_DontShowOnScreen 标志,为此您必须执行以下操作:

  • 删除 main_GUI 中的 imageViewer.show()
  • 在 Viewer fixture 中添加 imageViewer.setAttribute(Qt.WA_DontShowOnScreen, True)imageViewer.show()

另一种选择是在 pytest.ini:

中将环境变量 QT_QPA_PLATFORM 设置为 "offscreen"
env = 
    D:QT_QPA_PLATFORM=offscreen

如果使用 pyproject.toml,将如下所示:

[tool.pytest.ini_options]
env = [
    "D:QT_QPA_PLATFORM=offscreen"
]

我发现已接受的解决方案并不完全有效(例如,您的鼠标仍处于移动状态,点击了某些内容,并且菜单栏菜单仍然出现)。

此解决方案似乎没有该问题。