From 9012623110f49635fff61d19a4f5bb779de91b99 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 2 Feb 2021 09:04:58 -0500 Subject: refactor: move test mixins to their own file --- tests/coveragetest.py | 32 +++----------------------------- tests/mixins.py | 39 +++++++++++++++++++++++++++++++++++++++ tests/test_api.py | 4 ++-- tests/test_testing.py | 3 ++- 4 files changed, 46 insertions(+), 32 deletions(-) create mode 100644 tests/mixins.py (limited to 'tests') diff --git a/tests/coveragetest.py b/tests/coveragetest.py index c4a46da9..eeabfb46 100644 --- a/tests/coveragetest.py +++ b/tests/coveragetest.py @@ -6,7 +6,6 @@ import contextlib import datetime import difflib -import functools import glob import os import os.path @@ -14,20 +13,19 @@ import random import re import shlex import sys -import types import pytest from unittest_mixins import EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin import coverage from coverage import env -from coverage.backunittest import TestCase, unittest +from coverage.backunittest import TestCase from coverage.backward import StringIO, import_local_file, string_class, shlex_quote from coverage.cmdline import CoverageScript -from coverage.misc import StopEverything from tests.helpers import arcs_to_arcz_repr, arcz_to_arcs from tests.helpers import run_command, SuperModuleCleaner +from tests.mixins import StopEverythingMixin # Status returns for the command line. @@ -37,35 +35,11 @@ OK, ERR = 0, 1 TESTS_DIR = os.path.dirname(__file__) -def convert_skip_exceptions(method): - """A decorator for test methods to convert StopEverything to SkipTest.""" - @functools.wraps(method) - def _wrapper(*args, **kwargs): - try: - result = method(*args, **kwargs) - except StopEverything: - raise unittest.SkipTest("StopEverything!") - return result - return _wrapper - - -class SkipConvertingMetaclass(type): - """Decorate all test methods to convert StopEverything to SkipTest.""" - def __new__(cls, name, bases, attrs): - for attr_name, attr_value in attrs.items(): - if attr_name.startswith('test_') and isinstance(attr_value, types.FunctionType): - attrs[attr_name] = convert_skip_exceptions(attr_value) - - return super(SkipConvertingMetaclass, cls).__new__(cls, name, bases, attrs) - - -CoverageTestMethodsMixin = SkipConvertingMetaclass('CoverageTestMethodsMixin', (), {}) - class CoverageTest( EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin, - CoverageTestMethodsMixin, + StopEverythingMixin, TestCase, ): """A base class for coverage.py test cases.""" diff --git a/tests/mixins.py b/tests/mixins.py new file mode 100644 index 00000000..97ca093c --- /dev/null +++ b/tests/mixins.py @@ -0,0 +1,39 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +""" +Test class mixins + +Some of these are transitional while working toward pure-pytest style. +""" + +import functools +import types + +from coverage.backunittest import unittest +from coverage.misc import StopEverything + + +def convert_skip_exceptions(method): + """A decorator for test methods to convert StopEverything to SkipTest.""" + @functools.wraps(method) + def _wrapper(*args, **kwargs): + try: + result = method(*args, **kwargs) + except StopEverything: + raise unittest.SkipTest("StopEverything!") + return result + return _wrapper + + +class SkipConvertingMetaclass(type): + """Decorate all test methods to convert StopEverything to SkipTest.""" + def __new__(cls, name, bases, attrs): + for attr_name, attr_value in attrs.items(): + if attr_name.startswith('test_') and isinstance(attr_value, types.FunctionType): + attrs[attr_name] = convert_skip_exceptions(attr_value) + + return super(SkipConvertingMetaclass, cls).__new__(cls, name, bases, attrs) + + +StopEverythingMixin = SkipConvertingMetaclass('StopEverythingMixin', (), {}) diff --git a/tests/test_api.py b/tests/test_api.py index bce431f3..6c322795 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -22,7 +22,7 @@ from coverage.data import line_counts from coverage.files import abs_file, relative_filename from coverage.misc import CoverageException -from tests.coveragetest import CoverageTest, CoverageTestMethodsMixin, TESTS_DIR, UsingModulesMixin +from tests.coveragetest import CoverageTest, StopEverythingMixin, TESTS_DIR, UsingModulesMixin class ApiTest(CoverageTest): @@ -794,7 +794,7 @@ class NamespaceModuleTest(UsingModulesMixin, CoverageTest): cov.report() -class IncludeOmitTestsMixin(UsingModulesMixin, CoverageTestMethodsMixin): +class IncludeOmitTestsMixin(UsingModulesMixin, StopEverythingMixin): """Test methods for coverage methods taking include and omit.""" # We don't write any source files, but the data file will collide with diff --git a/tests/test_testing.py b/tests/test_testing.py index 21e09dcc..b6ffe1a0 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -17,11 +17,12 @@ from coverage.backunittest import TestCase, unittest from coverage.files import actual_path from coverage.misc import StopEverything -from tests.coveragetest import CoverageTest, convert_skip_exceptions +from tests.coveragetest import CoverageTest from tests.helpers import ( arcs_to_arcz_repr, arcz_to_arcs, CheckUniqueFilenames, re_lines, re_line, without_module, ) +from tests.mixins import convert_skip_exceptions def test_xdist_sys_path_nuttiness_is_fixed(): -- cgit v1.2.1 From 80c021d9174e7ae3e5183f1902903fb90a891246 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 2 Feb 2021 09:12:47 -0500 Subject: refactor: remove reliance on unittest_mixins.StdStreamCapturingMixin This is another step toward removing unittest.TestCase as a base class. --- tests/coveragetest.py | 4 ++-- tests/mixins.py | 31 +++++++++++++++++++++++++++++++ tests/test_api.py | 8 ++------ tests/test_arcs.py | 5 +++-- tests/test_cmdline.py | 10 ++++++---- tests/test_execfile.py | 30 ++++++++++++++++++------------ 6 files changed, 62 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/tests/coveragetest.py b/tests/coveragetest.py index eeabfb46..71f4e2c8 100644 --- a/tests/coveragetest.py +++ b/tests/coveragetest.py @@ -15,7 +15,7 @@ import shlex import sys import pytest -from unittest_mixins import EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin +from unittest_mixins import EnvironmentAwareMixin, TempDirMixin import coverage from coverage import env @@ -25,7 +25,7 @@ from coverage.cmdline import CoverageScript from tests.helpers import arcs_to_arcz_repr, arcz_to_arcs from tests.helpers import run_command, SuperModuleCleaner -from tests.mixins import StopEverythingMixin +from tests.mixins import StdStreamCapturingMixin, StopEverythingMixin # Status returns for the command line. diff --git a/tests/mixins.py b/tests/mixins.py index 97ca093c..ab0623c0 100644 --- a/tests/mixins.py +++ b/tests/mixins.py @@ -10,6 +10,8 @@ Some of these are transitional while working toward pure-pytest style. import functools import types +import pytest + from coverage.backunittest import unittest from coverage.misc import StopEverything @@ -37,3 +39,32 @@ class SkipConvertingMetaclass(type): StopEverythingMixin = SkipConvertingMetaclass('StopEverythingMixin', (), {}) + + +class StdStreamCapturingMixin: + """ + Adapter from the pytest capsys fixture to more convenient methods. + + This doesn't also output to the real stdout, so we probably want to move + to "real" capsys when we can use fixtures in test methods. + + Once you've used one of these methods, the capturing is reset, so another + invocation will only return the delta. + + """ + @pytest.fixture(autouse=True) + def _capcapsys(self, capsys): + """Grab the fixture so our methods can use it.""" + self.capsys = capsys + + def stdouterr(self): + """Returns (out, err), two strings for stdout and stderr.""" + return self.capsys.readouterr() + + def stdout(self): + """Returns a string, the captured stdout.""" + return self.capsys.readouterr().out + + def stderr(self): + """Returns a string, the captured stderr.""" + return self.capsys.readouterr().err diff --git a/tests/test_api.py b/tests/test_api.py index 6c322795..ea625ff1 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -520,10 +520,8 @@ class ApiTest(CoverageTest): self.start_import_stop(cov, "hello") cov.get_data() - out = self.stdout() + out, err = self.stdouterr() assert "Hello\n" in out - - err = self.stderr() assert textwrap.dedent("""\ Coverage.py warning: Module sys has no Python source. (module-not-python) Coverage.py warning: Module xyzzy was never imported. (module-not-imported) @@ -544,10 +542,8 @@ class ApiTest(CoverageTest): self.start_import_stop(cov, "hello") cov.get_data() - out = self.stdout() + out, err = self.stdouterr() assert "Hello\n" in out - - err = self.stderr() assert "Coverage.py warning: Module sys has no Python source. (module-not-python)" in err assert "module-not-imported" not in err assert "no-data-collected" not in err diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 3b63bcc2..66777751 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -1370,7 +1370,8 @@ class MiscArcTest(CoverageTest): # opcodes. # Note that we no longer interpret bytecode at all, but it couldn't # hurt to keep the test... - for n in [10, 50, 100, 500, 1000, 2000]: + sizes = [10, 50, 100, 500, 1000, 2000] + for n in sizes: code = """\ data = [ """ + "".join("""\ @@ -1383,7 +1384,7 @@ class MiscArcTest(CoverageTest): print(len(data)) """ self.check_coverage(code, arcs=[(-1, 1), (1, 2*n+4), (2*n+4, -1)]) - assert self.stdout().split()[-1] == str(n) + assert self.stdout().split() == [str(n) for n in sizes] def test_partial_generators(self): # https://github.com/nedbat/coveragepy/issues/475 diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index a0744452..0eb26cd0 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -545,8 +545,9 @@ class CmdLineTest(BaseCmdLineTest): # config file. self.command_line("run --concurrency=multiprocessing --branch foo.py", ret=ERR) msg = "Options affecting multiprocessing must only be specified in a configuration file." - assert msg in self.stderr() - assert "Remove --branch from the command line." in self.stderr() + _, err = self.stdouterr() + assert msg in err + assert "Remove --branch from the command line." in err def test_run_debug(self): self.cmd_executes("run --debug=opt1 foo.py", """\ @@ -915,8 +916,9 @@ class CmdMainTest(CoverageTest): def test_raise(self): ret = coverage.cmdline.main(['raise']) assert ret == 1 - assert self.stdout() == "" - err = self.stderr().split('\n') + out, err = self.stdouterr() + assert out == "" + err = err.split('\n') assert err[0] == 'Traceback (most recent call last):' assert err[-3] == ' raise Exception("oh noes!")' assert err[-2] == 'Exception: oh noes!' diff --git a/tests/test_execfile.py b/tests/test_execfile.py index db78d0f6..d221f2d3 100644 --- a/tests/test_execfile.py +++ b/tests/test_execfile.py @@ -193,33 +193,39 @@ class RunModuleTest(UsingModulesMixin, CoverageTest): def test_runmod1(self): run_python_module(["runmod1", "hello"]) - assert self.stderr() == "" - assert self.stdout() == "runmod1: passed hello\n" + out, err = self.stdouterr() + assert out == "runmod1: passed hello\n" + assert err == "" def test_runmod2(self): run_python_module(["pkg1.runmod2", "hello"]) - assert self.stderr() == "" - assert self.stdout() == "pkg1.__init__: pkg1\nrunmod2: passed hello\n" + out, err = self.stdouterr() + assert out == "pkg1.__init__: pkg1\nrunmod2: passed hello\n" + assert err == "" def test_runmod3(self): run_python_module(["pkg1.sub.runmod3", "hello"]) - assert self.stderr() == "" - assert self.stdout() == "pkg1.__init__: pkg1\nrunmod3: passed hello\n" + out, err = self.stdouterr() + assert out == "pkg1.__init__: pkg1\nrunmod3: passed hello\n" + assert err == "" def test_pkg1_main(self): run_python_module(["pkg1", "hello"]) - assert self.stderr() == "" - assert self.stdout() == "pkg1.__init__: pkg1\npkg1.__main__: passed hello\n" + out, err = self.stdouterr() + assert out == "pkg1.__init__: pkg1\npkg1.__main__: passed hello\n" + assert err == "" def test_pkg1_sub_main(self): run_python_module(["pkg1.sub", "hello"]) - assert self.stderr() == "" - assert self.stdout() == "pkg1.__init__: pkg1\npkg1.sub.__main__: passed hello\n" + out, err = self.stdouterr() + assert out == "pkg1.__init__: pkg1\npkg1.sub.__main__: passed hello\n" + assert err == "" def test_pkg1_init(self): run_python_module(["pkg1.__init__", "wut?"]) - assert self.stderr() == "" - assert self.stdout() == "pkg1.__init__: pkg1\npkg1.__init__: __main__\n" + out, err = self.stdouterr() + assert out == "pkg1.__init__: pkg1\npkg1.__init__: __main__\n" + assert err == "" def test_no_such_module(self): with pytest.raises(NoSource, match="No module named '?i_dont_exist'?"): -- cgit v1.2.1