Skip to content

Commit d1fd7c0

Browse files
committed
exceptions raised inside QtVirtual methods now make tests fail
fixes #11
1 parent 7b0a65a commit d1fd7c0

File tree

2 files changed

+72
-5
lines changed

2 files changed

+72
-5
lines changed

pytestqt/_tests/test_exceptions.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
from pytestqt.qt_compat import QtGui, Qt, QtCore
3+
4+
5+
class Receiver(QtCore.QObject):
6+
7+
def __init__(self, raise_error, *args, **kwargs):
8+
QtCore.QObject.__init__(self, *args, **kwargs)
9+
self._raise_error = raise_error
10+
11+
12+
def event(self, ev):
13+
if self._raise_error:
14+
raise ValueError('mistakes were made')
15+
return QtCore.QObject.event(self, ev)
16+
17+
18+
@pytest.mark.parametrize('raise_error', [False, pytest.mark.xfail(True)])
19+
def test_catch_exceptions_in_virtual_methods(qtbot, raise_error):
20+
"""
21+
Catch exceptions that happen inside Qt virtual methods and make the
22+
tests fail if any.
23+
"""
24+
v = Receiver(raise_error)
25+
app = QtGui.QApplication.instance()
26+
app.sendEvent(v, QtCore.QEvent(QtCore.QEvent.User))
27+
app.sendEvent(v, QtCore.QEvent(QtCore.QEvent.User))
28+
app.processEvents()
29+
30+

pytestqt/plugin.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
from contextlib import contextmanager
2+
import sys
3+
import traceback
4+
15
import pytest
2-
from pytestqt.qt_compat import QtGui
6+
7+
from pytestqt.qt_compat import QtGui
38
from pytestqt.qt_compat import QtTest
49

510

@@ -208,9 +213,30 @@ def stopForInteraction(self):
208213
stop = stopForInteraction
209214

210215

216+
@contextmanager
217+
def _capture_exceptions(self):
218+
"""
219+
Context manager that captures exceptions that happen insides it context,
220+
and returns them as a list of (type, value, traceback) after the
221+
context ends.
222+
"""
223+
result = []
224+
225+
def hook(type_, value, tback):
226+
result.append((type_, value, tback))
227+
sys.__excepthook__(type_, value, tback)
228+
229+
sys.excepthook = hook
230+
try:
231+
yield result
232+
finally:
233+
sys.excepthook = sys.__excepthook__
234+
235+
211236
def pytest_configure(config):
212237
"""
213-
PyTest plugin API. Called before the start of each test session.
238+
PyTest plugin API. Called before the start of each test session, used
239+
to instantiate the qApplication object that will be used for the session.
214240
215241
:param config.Config config:
216242
"""
@@ -226,7 +252,7 @@ def exit_qapp():
226252
config._cleanup.append(exit_qapp)
227253

228254

229-
@pytest.fixture
255+
@pytest.yield_fixture
230256
def qtbot(request):
231257
"""
232258
Fixture used to create a QtBot instance for using during testing.
@@ -235,6 +261,17 @@ def qtbot(request):
235261
that they are properly closed after the test ends.
236262
"""
237263
result = QtBot(request.config.qt_app_instance)
238-
request.addfinalizer(result._close)
239-
return result
264+
with result._capture_exceptions() as exceptions:
265+
yield result
266+
267+
if exceptions:
268+
message = 'Qt exceptions in virtual methods:\n'
269+
message += '_' * 80 + '\n'
270+
for (exc_type, value, tback) in exceptions:
271+
message += ''.join(traceback.format_tb(tback)) + '\n'
272+
message += '%s: %s\n' % (exc_type.__name__, value)
273+
message += '_' * 80 + '\n'
274+
pytest.fail(message)
275+
276+
result._close()
240277

0 commit comments

Comments
 (0)