diff --git a/.github/issue_template.md b/.github/issue_template.md
index 8bc43705..15aac278 100644
--- a/.github/issue_template.md
+++ b/.github/issue_template.md
@@ -29,6 +29,6 @@
* Version of spyder-unittest plugin:
* Installation method for Spyder and the unittest plugin: Anaconda / pip / ...
* Python version:
-* Testing framework used: nose / py.test / unittest
+* Testing framework used: nose2 / pytest / unittest
* Testing framework version:
* Operating system:
diff --git a/README.md b/README.md
index 304d76db..d0930b5f 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Spyder-unittest is a plugin that integrates popular unit test frameworks
with Spyder, allowing you to run test suites and view the results in the IDE.
The plugin supports the `unittest` module in the Python standard library
-as well as the `pytest` and `nose` testing frameworks.
+as well as the `pytest` and `nose2` testing frameworks.
Support for `pytest` is most complete at the moment.
@@ -76,10 +76,10 @@ The plugin has the following dependencies:
* [spyder](https://github.com/spyder-ide/spyder) (obviously), at least version 4.0
* [lxml](http://lxml.de/)
* the testing framework that you will be using: [pytest](https://pytest.org)
- and/or [nose](https://nose.readthedocs.io)
+ and/or [nose2](https://docs.nose2.io)
In order to run the tests distributed with this plugin, you need
-[nose](https://nose.readthedocs.io), [pytest](https://pytest.org)
+[nose2](https://docs.nose2.io), [pytest](https://pytest.org)
and [pytest-qt](https://github.com/pytest-dev/pytest-qt). If you use Python 2,
you also need [mock](https://github.com/testing-cabal/mock).
diff --git a/requirements/tests.txt b/requirements/tests.txt
index 00e208ac..ed20f926 100644
--- a/requirements/tests.txt
+++ b/requirements/tests.txt
@@ -1,5 +1,5 @@
flaky
-nose
+nose2
pytest>=5
pytest-cov
pytest-qt
diff --git a/setup.py b/setup.py
index 6fa09873..e4401799 100644
--- a/setup.py
+++ b/setup.py
@@ -47,7 +47,7 @@ def get_package_data(name, extlist):
frameworks. It allows you to run tests and view the results.
The plugin supports the `unittest` framework in the Python
-standard library and the `pytest` and `nose` testing frameworks.
+standard library and the `pytest` and `nose2` testing frameworks.
"""
setup(
diff --git a/spyder_unittest/backend/noserunner.py b/spyder_unittest/backend/nose2runner.py
similarity index 90%
rename from spyder_unittest/backend/noserunner.py
rename to spyder_unittest/backend/nose2runner.py
index 22d0a954..5ad8dd82 100644
--- a/spyder_unittest/backend/noserunner.py
+++ b/spyder_unittest/backend/nose2runner.py
@@ -19,18 +19,18 @@
_ = gettext.gettext
-class NoseRunner(RunnerBase):
+class Nose2Runner(RunnerBase):
"""Class for running tests within Nose framework."""
- module = 'nose'
- name = 'nose'
+ module = 'nose2'
+ name = 'nose2'
def create_argument_list(self, config, cov_path):
"""Create argument list for testing process."""
return [
- '-m', self.module, '--with-xunit',
- '--xunit-file={}'.format(self.resultfilename),
- ]
+ '-m', self.module, '--plugin=nose2.plugins.junitxml',
+ '--junit-xml', '--junit-xml-path={}'.format(self.resultfilename)
+ ]
def finished(self):
"""Called when the unit test process has finished."""
@@ -81,7 +81,7 @@ def load_data(self):
message = type_
if child.text:
extras.append(child.text)
- elif child.tag in ('system-out', 'system-err'):
+ elif child.tag in ('system-out', 'system-err') and child.text:
if child.tag == 'system-out':
heading = _('Captured stdout')
else:
diff --git a/spyder_unittest/backend/tests/test_nose2runner.py b/spyder_unittest/backend/tests/test_nose2runner.py
new file mode 100644
index 00000000..bc1eec57
--- /dev/null
+++ b/spyder_unittest/backend/tests/test_nose2runner.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2013 Spyder Project Contributors
+# Licensed under the terms of the MIT License
+# (see LICENSE.txt for details)
+"""Tests for nose2runner.py"""
+
+# Local imports
+from spyder_unittest.backend.nose2runner import Nose2Runner
+from spyder_unittest.backend.runnerbase import Category
+
+
+def test_nose2runner_load_data(tmpdir):
+ result_file = tmpdir.join('results')
+ result_txt = """
+
+
+
+
+
+ text
+
+
+"""
+ result_file.write(result_txt)
+ runner = Nose2Runner(None, result_file.strpath)
+ results = runner.load_data()
+ assert len(results) == 2
+
+ assert results[0].category == Category.OK
+ assert results[0].status == 'ok'
+ assert results[0].name == 'test_foo.test1'
+ assert results[0].message == ''
+ assert results[0].time == 0.04
+ assert results[0].extra_text == []
+
+ assert results[1].category == Category.FAIL
+ assert results[1].status == 'failure'
+ assert results[1].name == 'test_foo.test2'
+ assert results[1].message == 'test failure'
+ assert results[1].time == 0.01
+ assert results[1].extra_text == ['text']
diff --git a/spyder_unittest/backend/tests/test_noserunner.py b/spyder_unittest/backend/tests/test_noserunner.py
deleted file mode 100644
index af594914..00000000
--- a/spyder_unittest/backend/tests/test_noserunner.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2013 Spyder Project Contributors
-# Licensed under the terms of the MIT License
-# (see LICENSE.txt for details)
-"""Tests for noserunner.py"""
-
-# Local imports
-from spyder_unittest.backend.noserunner import NoseRunner
-from spyder_unittest.backend.runnerbase import Category
-
-
-def test_noserunner_load_data(tmpdir):
- result_file = tmpdir.join('results')
- result_txt = """
-
-
-
- text
-
-
- text2
-"""
- result_file.write(result_txt)
- runner = NoseRunner(None, result_file.strpath)
- results = runner.load_data()
- assert len(results) == 3
-
- assert results[0].category == Category.OK
- assert results[0].status == 'ok'
- assert results[0].name == 'test_foo.test1'
- assert results[0].message == ''
- assert results[0].time == 0.04
- assert results[0].extra_text == []
-
- assert results[1].category == Category.FAIL
- assert results[1].status == 'failure'
- assert results[1].name == 'test_foo.test2'
- assert results[1].message == 'failure message'
- assert results[1].time == 0.01
- assert results[1].extra_text == ['text']
-
- assert results[2].category == Category.SKIP
- assert results[2].status == 'skipped'
- assert results[2].name == 'test_foo.test3'
- assert results[2].message == 'skip message'
- assert results[2].time == 0.05
- assert results[2].extra_text == ['text2']
-
-
-def test_noserunner_load_data_failing_test_with_stdout(tmpdir):
- result_file = tmpdir.join('results')
- result_txt = """
-
-
-text
-stdout text
-"""
- result_file.write(result_txt)
- runner = NoseRunner(None, result_file.strpath)
- results = runner.load_data()
- assert results[0].extra_text == ['text', '', '----- Captured stdout -----', 'stdout text']
-
-
-def test_noserunner_load_data_passing_test_with_stdout(tmpdir):
- result_file = tmpdir.join('results')
- result_txt = """
-
-
-stdout text
-"""
- result_file.write(result_txt)
- runner = NoseRunner(None, result_file.strpath)
- results = runner.load_data()
- assert results[0].extra_text == ['----- Captured stdout -----', 'stdout text']
-
diff --git a/spyder_unittest/backend/workers/print_versions.py b/spyder_unittest/backend/workers/print_versions.py
index 6d049c27..0bc5809c 100644
--- a/spyder_unittest/backend/workers/print_versions.py
+++ b/spyder_unittest/backend/workers/print_versions.py
@@ -37,23 +37,21 @@ def pytest_cmdline_main(self, config):
'plugins': plugins}
-def get_nose_info():
- """Return information about nose."""
- from pkg_resources import iter_entry_points
+def get_nose2_info():
+ """
+ Return information about nose2.
+
+ This only returns the version of nose2. The function does not gather any
+ information about plugins.
+ """
try:
- import nose
+ import nose2
except ImportError:
return {'available': False}
- plugins = {}
- for entry_point, _ in (nose.plugins.manager.EntryPointPluginManager
- .entry_points):
- for ep in iter_entry_points(entry_point):
- plugins[ep.dist.project_name] = ep.dist.version
-
return {'available': True,
- 'version': nose.__version__,
- 'plugins': plugins}
+ 'version': nose2.__version__,
+ 'plugins': {}}
def get_unittest_info():
@@ -75,11 +73,11 @@ def get_all_info():
Information is returned as a dictionary like the following:
{'pytest': {'available': True, 'version': '7.1.1',
'plugins': {'flaky': '3.7.0', 'pytest-mock': '3.6.1'}},
- 'nose': {'available': False},
+ 'nose2': {'available': False},
'unittest': {'available': True, 'version': '3.10.5', 'plugins': {}}}
"""
return {'pytest': get_pytest_info(),
- 'nose': get_nose_info(),
+ 'nose2': get_nose2_info(),
'unittest': get_unittest_info()}
diff --git a/spyder_unittest/backend/workers/tests/test_print_versions.py b/spyder_unittest/backend/workers/tests/test_print_versions.py
index 06977931..3fd4ddb2 100644
--- a/spyder_unittest/backend/workers/tests/test_print_versions.py
+++ b/spyder_unittest/backend/workers/tests/test_print_versions.py
@@ -6,7 +6,7 @@
"""Tests for print_versions.py"""
from spyder_unittest.backend.workers.print_versions import (
- get_nose_info, get_pytest_info, get_unittest_info)
+ get_nose2_info, get_pytest_info, get_unittest_info)
def test_get_pytest_info_without_plugins(monkeypatch):
@@ -37,30 +37,11 @@ def test_get_pytest_info_with_plugins(monkeypatch):
assert get_pytest_info() == expected
-def test_get_nose_info_without_plugins(monkeypatch):
- import nose
- import pkg_resources
- monkeypatch.setattr(nose, '__version__', '1.2.3')
- monkeypatch.setattr(pkg_resources, 'iter_entry_points', lambda x: ())
+def test_get_nose2_info(monkeypatch):
+ import nose2
+ monkeypatch.setattr(nose2, '__version__', '1.2.3')
expected = {'available': True, 'version': '1.2.3', 'plugins': {}}
- assert get_nose_info() == expected
-
-
-def test_get_nose_info_with_plugins(monkeypatch):
- import nose
- import pkg_resources
- monkeypatch.setattr(nose, '__version__', '1.2.3')
- dist = pkg_resources.Distribution(project_name='myPlugin',
- version='4.5.6')
- ep = pkg_resources.EntryPoint('name', 'module_name', dist=dist)
- monkeypatch.setattr(pkg_resources,
- 'iter_entry_points',
- lambda ept: (x for x in (ep,) if ept == nose.plugins
- .manager.EntryPointPluginManager
- .entry_points[0][0]))
- expected = {'available': True, 'version': '1.2.3',
- 'plugins': {'myPlugin': '4.5.6'}}
- assert get_nose_info() == expected
+ assert get_nose2_info() == expected
def test_get_unittest_imfo(monkeypatch):
diff --git a/spyder_unittest/widgets/tests/test_unittestgui.py b/spyder_unittest/widgets/tests/test_unittestgui.py
index cecf1284..946f21d6 100644
--- a/spyder_unittest/widgets/tests/test_unittestgui.py
+++ b/spyder_unittest/widgets/tests/test_unittestgui.py
@@ -151,15 +151,15 @@ def test_unittestwidget_process_finished_abnormally_status_label(widget):
expected_text = '{}'.format('Test process exited abnormally')
assert widget.status_label.text() == expected_text
-@pytest.mark.parametrize('framework', ['pytest', 'nose'])
+@pytest.mark.parametrize('framework', ['pytest', 'nose2'])
def test_run_tests_and_display_results(qtbot, widget, tmpdir, monkeypatch, framework):
"""Basic integration test."""
os.chdir(tmpdir.strpath)
testfilename = tmpdir.join('test_foo.py').strpath
with open(testfilename, 'w') as f:
- f.write("def test_ok(): assert 1+1 == 2\n"
- "def test_fail(): assert 1+1 == 3\n")
+ f.write("def test_fail(): assert 1+1 == 3\n"
+ "def test_ok(): assert 1+1 == 2\n")
MockQMessageBox = Mock()
monkeypatch.setattr('spyder_unittest.widgets.unittestgui.QMessageBox',
@@ -173,14 +173,14 @@ def test_run_tests_and_display_results(qtbot, widget, tmpdir, monkeypatch, frame
model = widget.testdatamodel
assert model.rowCount() == 2
assert model.index(0, 0).data(
- Qt.DisplayRole) == 'ok' if framework == 'nose' else 'passed'
- assert model.index(0, 1).data(Qt.DisplayRole) == 't.test_ok'
- assert model.index(0, 1).data(Qt.ToolTipRole) == 'test_foo.test_ok'
- assert model.index(0, 2).data(Qt.DisplayRole) == ''
+ Qt.DisplayRole) == 'failure' if framework == 'nose2' else 'failed'
+ assert model.index(0, 1).data(Qt.DisplayRole) == 't.test_fail'
+ assert model.index(0, 1).data(Qt.ToolTipRole) == 'test_foo.test_fail'
assert model.index(1, 0).data(
- Qt.DisplayRole) == 'failure' if framework == 'nose' else 'failed'
- assert model.index(1, 1).data(Qt.DisplayRole) == 't.test_fail'
- assert model.index(1, 1).data(Qt.ToolTipRole) == 'test_foo.test_fail'
+ Qt.DisplayRole) == 'ok' if framework == 'nose2' else 'passed'
+ assert model.index(1, 1).data(Qt.DisplayRole) == 't.test_ok'
+ assert model.index(1, 1).data(Qt.ToolTipRole) == 'test_foo.test_ok'
+ assert model.index(1, 2).data(Qt.DisplayRole) == ''
def test_run_tests_using_unittest_and_display_results(
@@ -215,7 +215,7 @@ def test_run_tests_using_unittest_and_display_results(
assert model.index(1, 1).data(Qt.ToolTipRole) == 'test_foo.MyTest.test_ok'
assert model.index(1, 2).data(Qt.DisplayRole) == ''
-@pytest.mark.parametrize('framework', ['unittest', 'pytest', 'nose'])
+@pytest.mark.parametrize('framework', ['unittest', 'pytest', 'nose2'])
def test_run_with_no_tests_discovered_and_display_results(
qtbot, widget, tmpdir, monkeypatch, framework):
"""Basic integration test."""
diff --git a/spyder_unittest/widgets/unittestgui.py b/spyder_unittest/widgets/unittestgui.py
index b81e5f69..3125b8ef 100644
--- a/spyder_unittest/widgets/unittestgui.py
+++ b/spyder_unittest/widgets/unittestgui.py
@@ -22,7 +22,7 @@
# Local imports
from spyder_unittest.backend.frameworkregistry import FrameworkRegistry
-from spyder_unittest.backend.noserunner import NoseRunner
+from spyder_unittest.backend.nose2runner import Nose2Runner
from spyder_unittest.backend.pytestrunner import PyTestRunner
from spyder_unittest.backend.runnerbase import Category, TestResult
from spyder_unittest.backend.unittestrunner import UnittestRunner
@@ -37,7 +37,7 @@
_ = gettext.gettext
# Supported testing frameworks
-FRAMEWORKS = {NoseRunner, PyTestRunner, UnittestRunner}
+FRAMEWORKS = {Nose2Runner, PyTestRunner, UnittestRunner}
class UnitTestWidgetActions: