summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2021-03-07 17:51:38 -0500
committerNed Batchelder <ned@nedbatchelder.com>2021-03-11 06:38:42 -0500
commitb9f4c86917422de3fe6ecd2976d7213897c93bb2 (patch)
treec8da98046baddd68bcde6a0834f861e071a69a49
parentaf234f4a2a08dc1616c2270df6349925221c81e8 (diff)
downloadpython-coveragepy-git-b9f4c86917422de3fe6ecd2976d7213897c93bb2.tar.gz
test: reduce use of unittest
-rw-r--r--pylintrc4
-rw-r--r--tests/coveragetest.py30
-rw-r--r--tests/mixins.py131
-rw-r--r--tests/test_api.py8
-rw-r--r--tests/test_cmdline.py4
-rw-r--r--tests/test_files.py8
-rw-r--r--tests/test_html.py8
-rw-r--r--tests/test_numbits.py4
-rw-r--r--tests/test_oddball.py2
-rw-r--r--tests/test_process.py15
-rw-r--r--tests/test_setup.py4
11 files changed, 166 insertions, 52 deletions
diff --git a/pylintrc b/pylintrc
index c55b8982..1257838a 100644
--- a/pylintrc
+++ b/pylintrc
@@ -144,7 +144,7 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme
# TestCase overrides don't: setUp, tearDown
# Nested decorator implementations: _decorator, _wrapper
# Dispatched methods don't: _xxx__Xxxx
-no-docstring-rgx=__.*__|test[A-Z_].*|setUp|tearDown|_decorator|_wrapper|_.*__.*
+no-docstring-rgx=__.*__|test[A-Z_].*|setup_test|_decorator|_wrapper|_.*__.*
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
@@ -232,7 +232,7 @@ additional-builtins=
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp,reset
+defining-attr-methods=__init__,__new__,setup_test,reset
# checks for sign of poor/misdesign:
diff --git a/tests/coveragetest.py b/tests/coveragetest.py
index 0bfc7123..8427f4ad 100644
--- a/tests/coveragetest.py
+++ b/tests/coveragetest.py
@@ -13,10 +13,8 @@ import random
import re
import shlex
import sys
-import unittest
import pytest
-from unittest_mixins import TempDirMixin
import coverage
from coverage import env
@@ -25,7 +23,10 @@ from coverage.cmdline import CoverageScript
from tests.helpers import arcs_to_arcz_repr, arcz_to_arcs, assert_count_equal
from tests.helpers import run_command, SuperModuleCleaner
-from tests.mixins import EnvironmentAwareMixin, StdStreamCapturingMixin, StopEverythingMixin
+from tests.mixins import (
+ StdStreamCapturingMixin, StopEverythingMixin,
+ TempDirMixin, PytestBase,
+)
# Status returns for the command line.
@@ -36,11 +37,10 @@ TESTS_DIR = os.path.dirname(__file__)
class CoverageTest(
- EnvironmentAwareMixin,
StdStreamCapturingMixin,
TempDirMixin,
StopEverythingMixin,
- unittest.TestCase,
+ PytestBase,
):
"""A base class for coverage.py test cases."""
@@ -62,8 +62,8 @@ class CoverageTest(
# $set_env.py: COVERAGE_KEEP_TMP - Keep the temp directories made by tests.
keep_temp_dir = bool(int(os.getenv("COVERAGE_KEEP_TMP", "0")))
- def setUp(self):
- super(CoverageTest, self).setUp()
+ def setup_test(self):
+ super(CoverageTest, self).setup_test()
self.module_cleaner = SuperModuleCleaner()
@@ -187,7 +187,7 @@ class CoverageTest(
if statements == line_list:
break
else:
- self.fail("None of the lines choices matched %r" % statements)
+ assert False, "None of the lines choices matched %r" % (statements,)
missing_formatted = analysis.missing_formatted()
if isinstance(missing, string_class):
@@ -198,7 +198,7 @@ class CoverageTest(
if missing_formatted == missing_list:
break
else:
- self.fail("None of the missing choices matched %r" % missing_formatted)
+ assert False, "None of the missing choices matched %r" % (missing_formatted,)
if arcs is not None:
# print("Possible arcs:")
@@ -262,15 +262,17 @@ class CoverageTest(
if re.search(warning_regex, saved):
break
else:
- self.fail("Didn't find warning %r in %r" % (warning_regex, saved_warnings))
+ msg = "Didn't find warning %r in %r" % (warning_regex, saved_warnings)
+ assert False, msg
for warning_regex in not_warnings:
for saved in saved_warnings:
if re.search(warning_regex, saved):
- self.fail("Found warning %r in %r" % (warning_regex, saved_warnings))
+ msg = "Found warning %r in %r" % (warning_regex, saved_warnings)
+ assert False, msg
else:
# No warnings expected. Raise if any warnings happened.
if saved_warnings:
- self.fail("Unexpected warnings: %r" % (saved_warnings,))
+ assert False, "Unexpected warnings: %r" % (saved_warnings,)
finally:
cov._warn = original_warn
@@ -459,8 +461,8 @@ class CoverageTest(
class UsingModulesMixin(object):
"""A mixin for importing modules from tests/modules and tests/moremodules."""
- def setUp(self):
- super(UsingModulesMixin, self).setUp()
+ def setup_test(self):
+ super(UsingModulesMixin, self).setup_test()
# Parent class saves and restores sys.path, we can just modify it.
sys.path.append(self.nice_file(TESTS_DIR, 'modules'))
diff --git a/tests/mixins.py b/tests/mixins.py
index 5abaa759..8fe0690b 100644
--- a/tests/mixins.py
+++ b/tests/mixins.py
@@ -8,30 +8,143 @@ Some of these are transitional while working toward pure-pytest style.
"""
import functools
+import os
+import os.path
+import sys
import types
+import textwrap
import unittest
import pytest
+from coverage import env
from coverage.misc import StopEverything
-class EnvironmentAwareMixin:
- """
- Adapter from pytst monkeypatch fixture to our environment variable methods.
- """
+class PytestBase(object):
+ """A base class to connect to pytest in a test class hierarchy."""
+
@pytest.fixture(autouse=True)
- def _monkeypatch(self, monkeypatch):
- """Get the monkeypatch fixture for our methods to use."""
- self._envpatcher = monkeypatch
+ def connect_to_pytest(self, request, monkeypatch):
+ """Captures pytest facilities for use by other test helpers."""
+ # pylint: disable=attribute-defined-outside-init
+ self._pytest_request = request
+ self._monkeypatch = monkeypatch
+ self.setup_test()
+
+ # Can't call this setUp or setup because pytest sniffs out unittest and
+ # nosetest special names, and does things with them.
+ # https://github.com/pytest-dev/pytest/issues/8424
+ def setup_test(self):
+ """Per-test initialization. Override this as you wish."""
+ pass
+
+ def addCleanup(self, fn, *args):
+ """Like unittest's addCleanup: code to call when the test is done."""
+ self._pytest_request.addfinalizer(lambda: fn(*args))
def set_environ(self, name, value):
"""Set an environment variable `name` to be `value`."""
- self._envpatcher.setenv(name, value)
+ self._monkeypatch.setenv(name, value)
def del_environ(self, name):
"""Delete an environment variable, unless we set it."""
- self._envpatcher.delenv(name)
+ self._monkeypatch.delenv(name)
+
+
+class TempDirMixin(object):
+ """Provides temp dir and data file helpers for tests."""
+
+ # Our own setting: most of these tests run in their own temp directory.
+ # Set this to False in your subclass if you don't want a temp directory
+ # created.
+ run_in_temp_dir = True
+
+ # Set this if you aren't creating any files with make_file, but still want
+ # the temp directory. This will stop the test behavior checker from
+ # complaining.
+ no_files_in_temp_dir = False
+
+ @pytest.fixture(autouse=True)
+ def _temp_dir(self, tmpdir_factory):
+ """Create a temp dir for the tests, if they want it."""
+ old_dir = None
+ if self.run_in_temp_dir:
+ tmpdir = tmpdir_factory.mktemp("")
+ self.temp_dir = str(tmpdir)
+ old_dir = os.getcwd()
+ tmpdir.chdir()
+
+ # Modules should be importable from this temp directory. We don't
+ # use '' because we make lots of different temp directories and
+ # nose's caching importer can get confused. The full path prevents
+ # problems.
+ sys.path.insert(0, os.getcwd())
+
+ try:
+ yield None
+ finally:
+ if old_dir is not None:
+ os.chdir(old_dir)
+
+ @pytest.fixture(autouse=True)
+ def _save_sys_path(self):
+ """Restore sys.path at the end of each test."""
+ old_syspath = sys.path[:]
+ try:
+ yield
+ finally:
+ sys.path = old_syspath
+
+ @pytest.fixture(autouse=True)
+ def _module_saving(self):
+ """Remove modules we imported during the test."""
+ old_modules = list(sys.modules)
+ try:
+ yield
+ finally:
+ added_modules = [m for m in sys.modules if m not in old_modules]
+ for m in added_modules:
+ del sys.modules[m]
+
+ def make_file(self, filename, text="", bytes=b"", newline=None):
+ """Create a file for testing.
+
+ `filename` is the relative path to the file, including directories if
+ desired, which will be created if need be.
+
+ `text` is the content to create in the file, a native string (bytes in
+ Python 2, unicode in Python 3), or `bytes` are the bytes to write.
+
+ If `newline` is provided, it is a string that will be used as the line
+ endings in the created file, otherwise the line endings are as provided
+ in `text`.
+
+ Returns `filename`.
+
+ """
+ # pylint: disable=redefined-builtin # bytes
+ if bytes:
+ data = bytes
+ else:
+ text = textwrap.dedent(text)
+ if newline:
+ text = text.replace("\n", newline)
+ if env.PY3:
+ data = text.encode('utf8')
+ else:
+ data = text
+
+ # Make sure the directories are available.
+ dirs, _ = os.path.split(filename)
+ if dirs and not os.path.exists(dirs):
+ os.makedirs(dirs)
+
+ # Create the file.
+ with open(filename, 'wb') as f:
+ f.write(data)
+
+ return filename
def convert_skip_exceptions(method):
diff --git a/tests/test_api.py b/tests/test_api.py
index 391a52e0..a865c24c 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -884,7 +884,7 @@ class SourceIncludeOmitTest(IncludeOmitTestsMixin, CoverageTest):
assert lines['p1c'] == 0
def test_source_package_as_dir(self):
- self.chdir(self.nice_file(TESTS_DIR, 'modules'))
+ os.chdir(self.nice_file(TESTS_DIR, 'modules'))
assert os.path.isdir("pkg1")
lines = self.coverage_usepkgs(source=["pkg1"])
self.filenames_in(lines, "p1a p1b")
@@ -910,7 +910,7 @@ class SourceIncludeOmitTest(IncludeOmitTestsMixin, CoverageTest):
# the search for unexecuted files, and given a score of 0%.
# The omit arg is by path, so need to be in the modules directory.
- self.chdir(self.nice_file(TESTS_DIR, 'modules'))
+ os.chdir(self.nice_file(TESTS_DIR, 'modules'))
lines = self.coverage_usepkgs(source=["pkg1"], omit=["pkg1/p1b.py"])
self.filenames_in(lines, "p1a")
self.filenames_not_in(lines, "p1b")
@@ -925,7 +925,7 @@ class SourceIncludeOmitTest(IncludeOmitTestsMixin, CoverageTest):
def test_ambiguous_source_package_as_dir(self):
# pkg1 is a directory and a pkg, since we cd into tests/modules/ambiguous
- self.chdir(self.nice_file(TESTS_DIR, 'modules', "ambiguous"))
+ os.chdir(self.nice_file(TESTS_DIR, 'modules', "ambiguous"))
# pkg1 defaults to directory because tests/modules/ambiguous/pkg1 exists
lines = self.coverage_usepkgs(source=["pkg1"])
self.filenames_in(lines, "ambiguous")
@@ -933,7 +933,7 @@ class SourceIncludeOmitTest(IncludeOmitTestsMixin, CoverageTest):
def test_ambiguous_source_package_as_package(self):
# pkg1 is a directory and a pkg, since we cd into tests/modules/ambiguous
- self.chdir(self.nice_file(TESTS_DIR, 'modules', "ambiguous"))
+ os.chdir(self.nice_file(TESTS_DIR, 'modules', "ambiguous"))
lines = self.coverage_usepkgs(source_pkgs=["pkg1"])
self.filenames_in(lines, "p1a p1b")
self.filenames_not_in(lines, "p2a p2b othera otherb osa osb ambiguous")
diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
index d5141028..5c5ea0ef 100644
--- a/tests/test_cmdline.py
+++ b/tests/test_cmdline.py
@@ -900,8 +900,8 @@ class CmdMainTest(CoverageTest):
raise AssertionError("Bad CoverageScriptStub: %r" % (argv,))
return 0
- def setUp(self):
- super(CmdMainTest, self).setUp()
+ def setup_test(self):
+ super(CmdMainTest, self).setup_test()
old_CoverageScript = coverage.cmdline.CoverageScript
coverage.cmdline.CoverageScript = self.CoverageScriptStub
self.addCleanup(setattr, coverage.cmdline, 'CoverageScript', old_CoverageScript)
diff --git a/tests/test_files.py b/tests/test_files.py
index 6040b889..512e4294 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -41,7 +41,7 @@ class FilesTest(CoverageTest):
a1 = self.abs_path("sub/proj1/file1.py")
a2 = self.abs_path("sub/proj2/file2.py")
d = os.path.normpath("sub/proj1")
- self.chdir(d)
+ os.chdir(d)
files.set_relative_directory()
assert files.relative_filename(a1) == "file1.py"
assert files.relative_filename(a2) == a2
@@ -60,7 +60,7 @@ class FilesTest(CoverageTest):
def test_canonical_filename_ensure_cache_hit(self):
self.make_file("sub/proj1/file1.py")
d = actual_path(self.abs_path("sub/proj1"))
- self.chdir(d)
+ os.chdir(d)
files.set_relative_directory()
canonical_path = files.canonical_filename('sub/proj1/file1.py')
assert canonical_path == self.abs_path('file1.py')
@@ -140,8 +140,8 @@ def test_fnmatches_to_regex(patterns, case_insensitive, partial, matches, nomatc
class MatcherTest(CoverageTest):
"""Tests of file matchers."""
- def setUp(self):
- super(MatcherTest, self).setUp()
+ def setup_test(self):
+ super(MatcherTest, self).setup_test()
files.set_relative_directory()
def assertMatches(self, matcher, filepath, matches):
diff --git a/tests/test_html.py b/tests/test_html.py
index 79d14d26..1015b7d6 100644
--- a/tests/test_html.py
+++ b/tests/test_html.py
@@ -113,8 +113,8 @@ class FileWriteTracker(object):
class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
"""Tests of the HTML delta speed-ups."""
- def setUp(self):
- super(HtmlDeltaTest, self).setUp()
+ def setup_test(self):
+ super(HtmlDeltaTest, self).setup_test()
# At least one of our tests monkey-patches the version of coverage.py,
# so grab it here to restore it later.
@@ -555,8 +555,8 @@ class HtmlTest(HtmlTestHelpers, CoverageTest):
class HtmlStaticFileTest(CoverageTest):
"""Tests of the static file copying for the HTML report."""
- def setUp(self):
- super(HtmlStaticFileTest, self).setUp()
+ def setup_test(self):
+ super(HtmlStaticFileTest, self).setup_test()
original_path = list(coverage.html.STATIC_PATH)
self.addCleanup(setattr, coverage.html, 'STATIC_PATH', original_path)
diff --git a/tests/test_numbits.py b/tests/test_numbits.py
index fc27a093..946f8fcb 100644
--- a/tests/test_numbits.py
+++ b/tests/test_numbits.py
@@ -99,8 +99,8 @@ class NumbitsSqliteFunctionTest(CoverageTest):
run_in_temp_dir = False
- def setUp(self):
- super(NumbitsSqliteFunctionTest, self).setUp()
+ def setup_test(self):
+ super(NumbitsSqliteFunctionTest, self).setup_test()
conn = sqlite3.connect(":memory:")
register_sqlite_functions(conn)
self.cursor = conn.cursor()
diff --git a/tests/test_oddball.py b/tests/test_oddball.py
index b7307887..da0531f1 100644
--- a/tests/test_oddball.py
+++ b/tests/test_oddball.py
@@ -186,7 +186,7 @@ class MemoryLeakTest(CoverageTest):
fails += 1 # pragma: only failure
if fails > 8:
- self.fail("RAM grew by %d" % (ram_growth)) # pragma: only failure
+ pytest.fail("RAM grew by %d" % (ram_growth)) # pragma: only failure
class MemoryFumblingTest(CoverageTest):
diff --git a/tests/test_process.py b/tests/test_process.py
index 548f3dd7..0743e14e 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -1225,8 +1225,8 @@ class PydocTest(CoverageTest):
class FailUnderTest(CoverageTest):
"""Tests of the --fail-under switch."""
- def setUp(self):
- super(FailUnderTest, self).setUp()
+ def setup_test(self):
+ super(FailUnderTest, self).setup_test()
self.make_file("forty_two_plus.py", """\
# I have 42.857% (3/7) coverage!
a = 1
@@ -1448,8 +1448,8 @@ def persistent_remove(path):
class ProcessCoverageMixin(object):
"""Set up a .pth file to coverage-measure all sub-processes."""
- def setUp(self):
- super(ProcessCoverageMixin, self).setUp()
+ def setup_test(self):
+ super(ProcessCoverageMixin, self).setup_test()
# Create the .pth file.
assert PTH_DIR
@@ -1457,17 +1457,16 @@ class ProcessCoverageMixin(object):
pth_path = os.path.join(PTH_DIR, "subcover_{}.pth".format(WORKER))
with open(pth_path, "w") as pth:
pth.write(pth_contents)
- self.pth_path = pth_path
- self.addCleanup(persistent_remove, self.pth_path)
+ self.addCleanup(persistent_remove, pth_path)
@pytest.mark.skipif(env.METACOV, reason="Can't test sub-process pth file during metacoverage")
class ProcessStartupTest(ProcessCoverageMixin, CoverageTest):
"""Test that we can measure coverage in sub-processes."""
- def setUp(self):
- super(ProcessStartupTest, self).setUp()
+ def setup_test(self):
+ super(ProcessStartupTest, self).setup_test()
# Main will run sub.py
self.make_file("main.py", """\
diff --git a/tests/test_setup.py b/tests/test_setup.py
index 30456191..b2ccd67c 100644
--- a/tests/test_setup.py
+++ b/tests/test_setup.py
@@ -15,8 +15,8 @@ class SetupPyTest(CoverageTest):
run_in_temp_dir = False
- def setUp(self):
- super(SetupPyTest, self).setUp()
+ def setup_test(self):
+ super(SetupPyTest, self).setup_test()
# Force the most restrictive interpretation.
self.set_environ('LC_ALL', 'C')