Skip to content

Commit

Permalink
Handle BaseException in pytest_exception_interact (#22)
Browse files Browse the repository at this point in the history
When using pytest.raises() and the exception is not raised pytest will
raise an exception of the Failed class, which inherits from
BaseException, not Exception. This would cause an assertion error when
recoding the span, which causes pytest to crash
  • Loading branch information
ljodal authored Mar 10, 2023
1 parent 74c67f6 commit 0a3c179
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 6 deletions.
7 changes: 5 additions & 2 deletions src/pytest_opentelemetry/instrumentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,15 @@ def pytest_exception_interact(
) -> None:
excinfo = call.excinfo
assert excinfo
assert isinstance(excinfo.value, Exception)
assert isinstance(excinfo.value, BaseException)

test_span = trace.get_current_span()

test_span.record_exception(
exception=excinfo.value,
# Interface says Exception, but BaseException seems to work fine
# This is needed because pytest's Failed exception inherits from
# BaseException, not Exception
exception=excinfo.value, # type: ignore[arg-type]
attributes={
SpanAttributes.EXCEPTION_STACKTRACE: str(report.longrepr),
},
Expand Down
28 changes: 24 additions & 4 deletions tests/test_spans.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ def test_two():
def test_failures_and_errors(pytester: Pytester, span_recorder: SpanRecorder) -> None:
pytester.makepyfile(
"""
import pytest
def test_one():
assert 1 + 2 == 3
Expand All @@ -62,13 +64,18 @@ def test_two():
def test_three():
raise ValueError('woops')
def test_four():
# Test did not raise case
with pytest.raises(ValueError):
pass
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=1, failed=2)
result.assert_outcomes(passed=1, failed=3)

spans = span_recorder.spans_by_name()
assert len(spans) == 3 + 1
assert len(spans) == 4 + 1

span = spans['test run']
assert not span.status.is_ok
Expand All @@ -81,7 +88,7 @@ def test_three():
assert span.attributes
assert span.attributes['code.function'] == 'test_two'
assert span.attributes['code.filepath'] == 'test_failures_and_errors.py'
assert span.attributes['code.lineno'] == 3
assert span.attributes['code.lineno'] == 5
assert 'exception.stacktrace' not in span.attributes
assert len(span.events) == 1
event = span.events[0]
Expand All @@ -93,14 +100,27 @@ def test_three():
assert span.attributes
assert span.attributes['code.function'] == 'test_three'
assert span.attributes['code.filepath'] == 'test_failures_and_errors.py'
assert span.attributes['code.lineno'] == 6
assert span.attributes['code.lineno'] == 8
assert 'exception.stacktrace' not in span.attributes
assert len(span.events) == 1
event = span.events[0]
assert event.attributes
assert event.attributes['exception.type'] == 'ValueError'
assert event.attributes['exception.message'] == 'woops'

span = spans['test_four']
assert not span.status.is_ok
assert span.attributes
assert span.attributes['code.function'] == 'test_four'
assert span.attributes['code.filepath'] == 'test_failures_and_errors.py'
assert span.attributes['code.lineno'] == 11
assert 'exception.stacktrace' not in span.attributes
assert len(span.events) == 1
event = span.events[0]
assert event.attributes
assert event.attributes['exception.type'] == 'Failed'
assert event.attributes['exception.message'] == "DID NOT RAISE <class 'ValueError'>"


def test_failures_in_fixtures(pytester: Pytester, span_recorder: SpanRecorder) -> None:
pytester.makepyfile(
Expand Down

0 comments on commit 0a3c179

Please sign in to comment.