Add OuputVerbosity and use it in assertions
This commit is contained in:
parent
b73b4c464c
commit
c93bc1e0ca
1
AUTHORS
1
AUTHORS
|
@ -291,6 +291,7 @@ Ondřej Súkup
|
||||||
Oscar Benjamin
|
Oscar Benjamin
|
||||||
Parth Patel
|
Parth Patel
|
||||||
Patrick Hayes
|
Patrick Hayes
|
||||||
|
Patrick Lannigan
|
||||||
Paul Müller
|
Paul Müller
|
||||||
Paul Reece
|
Paul Reece
|
||||||
Pauli Virtanen
|
Pauli Virtanen
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
:confval:`verbosity_assertions` option added to be able to control assertion output independent of the application wide verbosity level.
|
|
@ -270,6 +270,16 @@ situations, for example you are shown even fixtures that start with ``_`` if you
|
||||||
Using higher verbosity levels (``-vvv``, ``-vvvv``, ...) is supported, but has no effect in pytest itself at the moment,
|
Using higher verbosity levels (``-vvv``, ``-vvvv``, ...) is supported, but has no effect in pytest itself at the moment,
|
||||||
however some plugins might make use of higher verbosity.
|
however some plugins might make use of higher verbosity.
|
||||||
|
|
||||||
|
Fine gain verbosity
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In addition to specifying the application wide verbosity level, it is possible to control specific aspects independently.
|
||||||
|
This is done by setting a verbosity level in the configuration file for the specific aspect of the output.
|
||||||
|
|
||||||
|
``verbosity_assertions``: Controls how verbose the assertion output should be when pytest is executed. A value of ``2``
|
||||||
|
would have the same output as the previous example, but each test inside the file is shown by a single character in the
|
||||||
|
output.
|
||||||
|
|
||||||
.. _`pytest.detailed_failed_tests_usage`:
|
.. _`pytest.detailed_failed_tests_usage`:
|
||||||
|
|
||||||
Producing a detailed summary report
|
Producing a detailed summary report
|
||||||
|
|
|
@ -1819,6 +1819,19 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||||
clean_db
|
clean_db
|
||||||
|
|
||||||
|
|
||||||
|
.. confval:: verbosity_assertions
|
||||||
|
|
||||||
|
Set a verbosity level specifically for assertion related output, overriding the application wide level.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
verbosity_assertions = 2
|
||||||
|
|
||||||
|
Defaults to application wide verbosity level.
|
||||||
|
|
||||||
|
|
||||||
.. confval:: xfail_strict
|
.. confval:: xfail_strict
|
||||||
|
|
||||||
If set to ``True``, tests marked with ``@pytest.mark.xfail`` that actually succeed will by default fail the
|
If set to ``True``, tests marked with ``@pytest.mark.xfail`` that actually succeed will by default fail the
|
||||||
|
|
|
@ -12,6 +12,7 @@ from _pytest.assertion import util
|
||||||
from _pytest.assertion.rewrite import assertstate_key
|
from _pytest.assertion.rewrite import assertstate_key
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
|
from _pytest.config import OutputVerbosity
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.nodes import Item
|
from _pytest.nodes import Item
|
||||||
|
|
||||||
|
@ -42,6 +43,14 @@ def pytest_addoption(parser: Parser) -> None:
|
||||||
help="Enables the pytest_assertion_pass hook. "
|
help="Enables the pytest_assertion_pass hook. "
|
||||||
"Make sure to delete any previously generated pyc cache files.",
|
"Make sure to delete any previously generated pyc cache files.",
|
||||||
)
|
)
|
||||||
|
OutputVerbosity.add_ini(
|
||||||
|
parser,
|
||||||
|
"assertions",
|
||||||
|
help=(
|
||||||
|
"Specify a verbosity level for assertions, overriding the main level. "
|
||||||
|
"Higher levels will provide more a more detailed explanation when an assertion fails."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def register_assert_rewrite(*names: str) -> None:
|
def register_assert_rewrite(*names: str) -> None:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Utilities for truncating assertion output.
|
"""Utilities for truncating assertion output.
|
||||||
|
|
||||||
Current default behaviour is to truncate assertion explanations at
|
Current default behaviour is to truncate assertion explanations at
|
||||||
~8 terminal lines, unless running in "-vv" mode or running on CI.
|
terminal lines, unless running with a verbosity level of at least 2 or running on CI.
|
||||||
"""
|
"""
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -26,7 +26,7 @@ def truncate_if_required(
|
||||||
|
|
||||||
def _should_truncate_item(item: Item) -> bool:
|
def _should_truncate_item(item: Item) -> bool:
|
||||||
"""Whether or not this test item is eligible for truncation."""
|
"""Whether or not this test item is eligible for truncation."""
|
||||||
verbose = item.config.option.verbose
|
verbose = item.config.output_verbosity.verbosity_for("assertions")
|
||||||
return verbose < 2 and not util.running_on_ci()
|
return verbose < 2 and not util.running_on_ci()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ def assertrepr_compare(
|
||||||
config, op: str, left: Any, right: Any, use_ascii: bool = False
|
config, op: str, left: Any, right: Any, use_ascii: bool = False
|
||||||
) -> Optional[List[str]]:
|
) -> Optional[List[str]]:
|
||||||
"""Return specialised explanations for some operators/operands."""
|
"""Return specialised explanations for some operators/operands."""
|
||||||
verbose = config.getoption("verbose")
|
verbose = config.output_verbosity.verbosity_for("assertions")
|
||||||
|
|
||||||
# Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier.
|
# Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier.
|
||||||
# See issue #3246.
|
# See issue #3246.
|
||||||
|
|
|
@ -69,7 +69,7 @@ from _pytest.warning_types import warn_explicit_for
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from _pytest._code.code import _TracebackStyle
|
from _pytest._code.code import _TracebackStyle
|
||||||
from _pytest.terminal import TerminalReporter
|
from _pytest.terminal import TerminalReporter
|
||||||
from .argparsing import Argument
|
from .argparsing import Argument, Parser
|
||||||
|
|
||||||
|
|
||||||
_PluggyPlugin = object
|
_PluggyPlugin = object
|
||||||
|
@ -1020,6 +1020,7 @@ class Config:
|
||||||
)
|
)
|
||||||
self.args_source = Config.ArgsSource.ARGS
|
self.args_source = Config.ArgsSource.ARGS
|
||||||
self.args: List[str] = []
|
self.args: List[str] = []
|
||||||
|
self.output_verbosity = OutputVerbosity(self)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from _pytest.cacheprovider import Cache
|
from _pytest.cacheprovider import Cache
|
||||||
|
@ -1662,6 +1663,54 @@ class Config:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OutputVerbosity:
|
||||||
|
DEFAULT = "auto"
|
||||||
|
_option_name_fmt = "verbosity_{}"
|
||||||
|
|
||||||
|
def __init__(self, config: Config) -> None:
|
||||||
|
self._config = config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def verbose(self) -> int:
|
||||||
|
"""Application wide verbosity level."""
|
||||||
|
return cast(int, self._config.option.verbose)
|
||||||
|
|
||||||
|
def verbosity_for(self, output_type: str) -> int:
|
||||||
|
"""Return verbosity level for the given output type.
|
||||||
|
|
||||||
|
:param output_type: Name of the output type.
|
||||||
|
|
||||||
|
If the level is not configured, the value of ``config.option.verbose``.
|
||||||
|
"""
|
||||||
|
level = self._config.getini(OutputVerbosity._ini_name(output_type))
|
||||||
|
|
||||||
|
if level == OutputVerbosity.DEFAULT:
|
||||||
|
return self.verbose
|
||||||
|
return int(level)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ini_name(output_type: str) -> str:
|
||||||
|
return f"verbosity_{output_type}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_ini(parser: "Parser", output_type: str, help: str) -> None:
|
||||||
|
"""Add a output verbosity configuration option for the given output type.
|
||||||
|
|
||||||
|
:param parser: Parser for command line arguments and ini-file values.
|
||||||
|
:param output_type: Name of the output type.
|
||||||
|
:param help: Description of the output this type controls.
|
||||||
|
|
||||||
|
The value should be retrieved via a call to
|
||||||
|
:py:func:`config.output_verbosity.verbosity_for(name) <pytest.OutputVerbosity.verbosity_for>`.
|
||||||
|
"""
|
||||||
|
parser.addini(
|
||||||
|
OutputVerbosity._ini_name(output_type),
|
||||||
|
help=help,
|
||||||
|
type="string",
|
||||||
|
default=OutputVerbosity.DEFAULT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _assertion_supported() -> bool:
|
def _assertion_supported() -> bool:
|
||||||
try:
|
try:
|
||||||
assert False
|
assert False
|
||||||
|
|
|
@ -15,6 +15,7 @@ from _pytest.config import ExitCode
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config import hookspec
|
from _pytest.config import hookspec
|
||||||
from _pytest.config import main
|
from _pytest.config import main
|
||||||
|
from _pytest.config import OutputVerbosity
|
||||||
from _pytest.config import PytestPluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.config import UsageError
|
from _pytest.config import UsageError
|
||||||
from _pytest.config.argparsing import OptionGroup
|
from _pytest.config.argparsing import OptionGroup
|
||||||
|
@ -126,6 +127,7 @@ __all__ = [
|
||||||
"Module",
|
"Module",
|
||||||
"MonkeyPatch",
|
"MonkeyPatch",
|
||||||
"OptionGroup",
|
"OptionGroup",
|
||||||
|
"OutputVerbosity",
|
||||||
"Package",
|
"Package",
|
||||||
"param",
|
"param",
|
||||||
"Parser",
|
"Parser",
|
||||||
|
|
|
@ -17,12 +17,23 @@ from _pytest.monkeypatch import MonkeyPatch
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
|
||||||
|
|
||||||
def mock_config(verbose=0):
|
def mock_config(verbose: int = 0, assertion_override: Optional[int] = None):
|
||||||
class Config:
|
class OutputVerbosity:
|
||||||
def getoption(self, name):
|
@property
|
||||||
if name == "verbose":
|
def verbose(self) -> int:
|
||||||
|
return verbose
|
||||||
|
|
||||||
|
def verbosity_for(self, output_type: str) -> int:
|
||||||
|
if output_type == "assertions":
|
||||||
|
if assertion_override is not None:
|
||||||
|
return assertion_override
|
||||||
return verbose
|
return verbose
|
||||||
raise KeyError("Not mocked out: %s" % name)
|
|
||||||
|
raise KeyError("Not mocked out: %s" % output_type)
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.output_verbosity = OutputVerbosity()
|
||||||
|
|
||||||
return Config()
|
return Config()
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,9 @@ from _pytest.config import _strtobool
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ConftestImportFailure
|
from _pytest.config import ConftestImportFailure
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
|
from _pytest.config import OutputVerbosity
|
||||||
from _pytest.config import parse_warning_filter
|
from _pytest.config import parse_warning_filter
|
||||||
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.config.exceptions import UsageError
|
from _pytest.config.exceptions import UsageError
|
||||||
from _pytest.config.findpaths import determine_setup
|
from _pytest.config.findpaths import determine_setup
|
||||||
from _pytest.config.findpaths import get_common_ancestor
|
from _pytest.config.findpaths import get_common_ancestor
|
||||||
|
@ -2181,3 +2183,74 @@ class TestDebugOptions:
|
||||||
"*Default: pytestdebug.log.",
|
"*Default: pytestdebug.log.",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestOutputVerbosity:
|
||||||
|
SOME_OUTPUT_TYPE = "foo"
|
||||||
|
SOME_OUTPUT_VERBOSITY_LEVEL = 5
|
||||||
|
|
||||||
|
class VerbosityIni:
|
||||||
|
def pytest_addoption(self, parser: Parser) -> None:
|
||||||
|
OutputVerbosity.add_ini(
|
||||||
|
parser, TestOutputVerbosity.SOME_OUTPUT_TYPE, help="some help text"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_verbose_matches_option_verbose(
|
||||||
|
self, pytester: Pytester, tmp_path: Path
|
||||||
|
) -> None:
|
||||||
|
tmp_path.joinpath("pytest.ini").write_text(
|
||||||
|
textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
[pytest]
|
||||||
|
addopts = --verbose
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
config = pytester.parseconfig(tmp_path)
|
||||||
|
|
||||||
|
assert config.option.verbose == config.output_verbosity.verbose
|
||||||
|
|
||||||
|
def test_level_matches_verbost_when_not_specified(
|
||||||
|
self, pytester: Pytester, tmp_path: Path
|
||||||
|
) -> None:
|
||||||
|
tmp_path.joinpath("pytest.ini").write_text(
|
||||||
|
textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
[pytest]
|
||||||
|
addopts = --verbose
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
pytester.plugins = [TestOutputVerbosity.VerbosityIni()]
|
||||||
|
|
||||||
|
config = pytester.parseconfig(tmp_path)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
config.output_verbosity.verbosity_for(TestOutputVerbosity.SOME_OUTPUT_TYPE)
|
||||||
|
== config.output_verbosity.verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_level_matches_specified_override(
|
||||||
|
self, pytester: Pytester, tmp_path: Path
|
||||||
|
) -> None:
|
||||||
|
tmp_path.joinpath("pytest.ini").write_text(
|
||||||
|
textwrap.dedent(
|
||||||
|
f"""\
|
||||||
|
[pytest]
|
||||||
|
addopts = --verbose
|
||||||
|
verbosity_{TestOutputVerbosity.SOME_OUTPUT_TYPE} = {TestOutputVerbosity.SOME_OUTPUT_VERBOSITY_LEVEL}
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
pytester.plugins = [TestOutputVerbosity.VerbosityIni()]
|
||||||
|
|
||||||
|
config = pytester.parseconfig(tmp_path)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
config.output_verbosity.verbosity_for(TestOutputVerbosity.SOME_OUTPUT_TYPE)
|
||||||
|
== TestOutputVerbosity.SOME_OUTPUT_VERBOSITY_LEVEL
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue