Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements/tests.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
flaky
nose2
pytest>=5
pytest>=7
pytest-qt
2 changes: 1 addition & 1 deletion spyder_unittest/backend/workers/pytestworker.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def initialize_logreport(self):
self.was_skipped = False
self.was_xfail = False

def pytest_report_header(self, config, startdir):
def pytest_report_header(self, config, start_path):
"""Called by pytest before any reporting."""
self.writer.write({
'event': 'config',
Expand Down
14 changes: 9 additions & 5 deletions spyder_unittest/backend/workers/tests/test_print_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# (see LICENSE.txt for details)
"""Tests for print_versions.py"""

from importlib.metadata import Distribution
from unittest.mock import MagicMock

from spyder_unittest.backend.workers.print_versions import (
get_nose2_info, get_pytest_info, get_unittest_info)

Expand All @@ -22,12 +25,13 @@ def test_get_pytest_info_without_plugins(monkeypatch):

def test_get_pytest_info_with_plugins(monkeypatch):
import pytest
import pkg_resources
monkeypatch.setattr(pytest, '__version__', '1.2.3')
dist1 = pkg_resources.Distribution(project_name='myPlugin1',
version='4.5.6')
dist2 = pkg_resources.Distribution(project_name='myPlugin2',
version='7.8.9')
dist1 = MagicMock(
autospec=Distribution, project_name='myPlugin1', version='4.5.6'
)
dist2 = MagicMock(
autospec=Distribution, project_name='myPlugin2', version='7.8.9'
)
from _pytest.config import PytestPluginManager
monkeypatch.setattr(
PytestPluginManager,
Expand Down
1 change: 0 additions & 1 deletion spyder_unittest/backend/workers/tests/test_pytestworker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
# Third party imports
import pytest

# Local imports
# Local imports
# Modules in spyder_unittest.backend.workers assume that their directory
# is in `sys.path`, so add that directory to the path.
Expand Down
17 changes: 17 additions & 0 deletions spyder_unittest/widgets/tests/test_unittestgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ def test_unittestwidget_tests_yield_results(widget):
widget.tests_yield_result(results)
widget.testdatamodel.update_testresults.assert_called_once_with(results)

def test_unittestwidget_tests_yield_results_with_error(widget):
"""
Test that if test_yield_result() raises a KeyError, a message is displayed,
but that no message is displayed on the second time.

Regression test for spyder-ide/spyder-unittest#233.
"""
use_mock_model(widget)
widget.testdatamodel.update_testresults = Mock(side_effect=KeyError)
results = [TestResult(Category.OK, 'ok', 'hammodule.spam')]
with patch('spyder_unittest.widgets.unittestgui.QMessageBox') as mockQMessageBox:
widget.tests_yield_result(results)
mockQMessageBox.critical.assert_called()
with patch('spyder_unittest.widgets.unittestgui.QMessageBox') as mockQMessageBox:
widget.tests_yield_result(results)
mockQMessageBox.critical.assert_not_called()

def test_unittestwidget_set_message(widget):
widget.status_label = Mock()
widget.set_status_label('xxx')
Expand Down
33 changes: 31 additions & 2 deletions spyder_unittest/widgets/unittestgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ class UnitTestWidget(PluginMainWidget):
Python interpreter for which `self.dependencies` is valid.
framework_registry : FrameworkRegistry
Registry of supported testing frameworks.
got_unexpected_testresult : bool
Whether we received the result of a test that was not collected in the
current test run.
pre_test_hook : function returning bool or None
If set, contains function to run before running tests; abort the test
run if hook returns False.
Expand Down Expand Up @@ -112,6 +115,7 @@ def __init__(self, name, plugin, parent):
self.pre_test_hook = None
self.pythonpath = None
self.testrunner = None
self.got_unexpected_testresult = False

self.testdataview = TestDataView(self)
self.testdatamodel = TestDataModel(self)
Expand Down Expand Up @@ -365,6 +369,7 @@ def run_tests(self, config=None, single_test=None):
if self.pre_test_hook() is False:
return

self.got_unexpected_testresult = False
if config is None:
config = self.config
pythonpath = self.pythonpath
Expand Down Expand Up @@ -473,7 +478,7 @@ def tests_started(self, testnames):
testresults = [TestResult(Category.PENDING, _('pending'), name,
message=_('running'))
for name in testnames]
self.testdatamodel.update_testresults(testresults)
self.update_testresults_safe(testresults)

def tests_collect_error(self, testnames_plus_msg):
"""Called when errors are encountered during collection."""
Expand All @@ -485,7 +490,31 @@ def tests_collect_error(self, testnames_plus_msg):

def tests_yield_result(self, testresults):
"""Called when test results are received."""
self.testdatamodel.update_testresults(testresults)
self.update_testresults_safe(testresults)

def update_testresults_safe(self, testresults: list[TestResult]):
"""
Update test results in data model and handle errors.

If a KeyError is raised (because one or more of the test results are
from test that were not collected earlier) and this is the first time
in the current test run, then display an dialog box explaining the
situation.
"""
try:
self.testdatamodel.update_testresults(testresults)
except KeyError:
if self.got_unexpected_testresult:
return
self.got_unexpected_testresult = True
msg = _(
"Spyder can not display the test results because it received "
"an unexpected test result."
"<p>"
"This may be caused by unsupported pytest plugins, "
"e.g., pytest-xdist."
)
QMessageBox.critical(self, _("Error"), msg)

def tests_stopped(self):
"""Called when tests are stopped"""
Expand Down
Loading