You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug
When using dynamic_context=test_function, the context is not set if the test function is a @staticmethod or @classmethod.
I believe this is due to the logic of context.qualname_from_frame, which explicitly tests for the presence of a first argument named "self" to detect whether a test function is scoped to a class. Since static and class methods do not tend to have such an argument, they are treated as functions, but because they are not found in f_globals, None is returned by qualname_from_frame and in turn returned by should_start_context_test_function, as if the method was not a test at all.
To Reproduce
Here is a minimal example that demonstrates the problem.
place the files below alongside each other in some directory
run tox in that directory
note that this warning is emitted CoverageWarning: No contexts were measured self.coverage._warn("No contexts were measured")
observe that the context for the lines of foo_module.py are labeled (empty) in the htmlcov report
[testenv]deps =
coverage
pytest
commands =
coverage run -m pytest
coverage html --show-contexts
Expected behavior
I would expect to see the contexts TestFoo.test_foo_static and TestFoo.test_foo_class in the htmlcov report, and no warning.
Additional context
Class methods can be covered by adding an explicit test for a first argument named "cls" similar to the existing logic.
Static methods are a little trickier. Python 3.11 adds the co_qualname attribute to frame.f_code, which is exactly what's needed, and solves the problem for me. For older Python versions, the status quo could be improved by simply returning something like staticmethod(test_foo_static).
For example, changing these lines as follows might work. The only caveat is that perhaps there's some other reason you could reach the func is None case.
Hi @nedbat , I mostly just use @staticmethod because it seemed nicer to me than having an unused self argument. It's probably a bit idiosyncratic, and I do know that support for @staticmethod was added to pytest quite late (2017), but I was still surprised when I eventually realised why coverage wasn't setting the dynamic context.
While I have you, would you be able to influence Canonical to provide some GitHub sponsorship for coverage.py?
Good idea, I'm not in charge of such things, but I'll ask around
Describe the bug
When using
dynamic_context=test_function
, the context is not set if the test function is a@staticmethod
or@classmethod
.I believe this is due to the logic of context.qualname_from_frame, which explicitly tests for the presence of a first argument named "self" to detect whether a test function is scoped to a class. Since static and class methods do not tend to have such an argument, they are treated as functions, but because they are not found in
f_globals
,None
is returned byqualname_from_frame
and in turn returned byshould_start_context_test_function
, as if the method was not a test at all.To Reproduce
Here is a minimal example that demonstrates the problem.
tox
in that directoryCoverageWarning: No contexts were measured self.coverage._warn("No contexts were measured")
foo_module.py
are labeled(empty)
in the htmlcov reportfoo_module.py
test_foo.py
.coveragerc
tox.ini
Expected behavior
I would expect to see the contexts
TestFoo.test_foo_static
andTestFoo.test_foo_class
in the htmlcov report, and no warning.Additional context
Class methods can be covered by adding an explicit test for a first argument named "cls" similar to the existing logic.
Static methods are a little trickier. Python 3.11 adds the
co_qualname
attribute toframe.f_code
, which is exactly what's needed, and solves the problem for me. For older Python versions, the status quo could be improved by simply returning something likestaticmethod(test_foo_static)
.For example, changing these lines as follows might work. The only caveat is that perhaps there's some other reason you could reach the
func is None
case.The text was updated successfully, but these errors were encountered: