pytest-qt
pytest-qt is a pytest plugin that provides utilities and fixtures for testing PyQt and PySide applications. It simplifies GUI testing by offering tools to interact with widgets, handle signals, and manage the Qt event loop, ensuring reliable and robust tests for Qt-based UIs. The current version is 4.5.0, with minor releases arriving semi-regularly.
Warnings
- breaking The environment variable `PYTEST_QT_FORCE_PYQT` for forcing PyQt API selection was deprecated in favor of `PYTEST_QT_API` in version 1.3.0. While `PYTEST_QT_FORCE_PYQT` was initially kept for backward compatibility, new projects and updated configurations should use `PYTEST_QT_API`.
- gotcha Prior to `pytest-qt` version 1.5.0, exceptions raised within a `qtbot.waitSignals` or `qtbot.waitSignal` with-statement block could be swallowed and result in a `SignalTimeoutError` instead of the actual exception, masking the root cause of the failure.
- breaking When using `PyQt5.5` or newer, Qt changed its default behavior to call `abort` on unhandled exceptions in virtual methods during tear down, which could crash the interpreter. `pytest-qt` version 1.5.1 introduced a fix to capture these exceptions during tear down, preventing crashes.
- gotcha While `pytest-qt` attempts to auto-detect the available Qt binding (PyQt5, PyQt6, PySide2, PySide6), explicit configuration is highly recommended to avoid unexpected behavior or conflicts if multiple bindings are installed. The order of `sys.path` and other factors can influence auto-detection.
Install
-
pip install pytest-qt -
pip install pytest-qt 'PyQt5' -
pip install pytest-qt 'PySide6'
Imports
- qtbot (fixture)
def test_something(qtbot): # use qtbot here
Quickstart
from PyQt5.QtWidgets import QLabel
import pytest
def test_label_text_using_qtbot(qtbot):
# The QApplication instance is managed by pytest-qt (via the 'qapp' fixture if needed).
# We typically don't create/quit QApplication explicitly in tests using pytest-qt.
label = QLabel('Hello QtBot')
qtbot.addWidget(label)
# qtbot.addWidget ensures the widget is shown and its lifecycle is managed for the test.
# No explicit label.show(), qtbot.waitExposed(), or label.close() needed for basic interaction.
assert label.text() == 'Hello QtBot'