Skip to content

Commit

Permalink
Merge pull request #200 from jitseniesen/nose
Browse files Browse the repository at this point in the history
PR: Support nose2 as testing framework instead of nose
  • Loading branch information
jitseniesen committed Apr 19, 2023
2 parents b640222 + b3ce196 commit a9e3cb5
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .github/issue_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.


Expand Down Expand Up @@ -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).

Expand Down
2 changes: 1 addition & 1 deletion requirements/tests.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
flaky
nose
nose2
pytest>=5
pytest-cov
pytest-qt
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand Down
42 changes: 42 additions & 0 deletions spyder_unittest/backend/tests/test_nose2runner.py
Original file line number Diff line number Diff line change
@@ -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 = """
<testsuite name="nose2-junit" errors="0" failures="1" skipped="0" tests="2" time="0.000">
<testcase time="0.04" classname="test_foo" name="test1">
<system-out />
</testcase>
<testcase time="0.01" classname="test_foo" name="test2">
<failure message="test failure">text</failure>
<system-out />
</testcase>
</testsuite>"""
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']
76 changes: 0 additions & 76 deletions spyder_unittest/backend/tests/test_noserunner.py

This file was deleted.

26 changes: 12 additions & 14 deletions spyder_unittest/backend/workers/print_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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()}


Expand Down
29 changes: 5 additions & 24 deletions spyder_unittest/backend/workers/tests/test_print_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
22 changes: 11 additions & 11 deletions spyder_unittest/widgets/tests/test_unittestgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ def test_unittestwidget_process_finished_abnormally_status_label(widget):
expected_text = '<b>{}</b>'.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',
Expand All @@ -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(
Expand Down Expand Up @@ -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."""
Expand Down
4 changes: 2 additions & 2 deletions spyder_unittest/widgets/unittestgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -37,7 +37,7 @@
_ = gettext.gettext

# Supported testing frameworks
FRAMEWORKS = {NoseRunner, PyTestRunner, UnittestRunner}
FRAMEWORKS = {Nose2Runner, PyTestRunner, UnittestRunner}


class UnitTestWidgetActions:
Expand Down

0 comments on commit a9e3cb5

Please sign in to comment.