From 4bac02e97ebe6d1eecab1d8beaf9a93e4671c384 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 25 Jan 2024 17:35:10 -0600 Subject: [PATCH] Add test context for no exception raised (#3630) * Add test context for no exception raised Fixes #1891 * Add missing test class --- .../tests/test_otlp.py | 10 +-- .../test_exponent_mapping.py | 7 +- ...xponential_bucket_histogram_aggregation.py | 31 +------ .../tests/metrics/test_backward_compat.py | 14 +--- .../tests/metrics/test_import.py | 14 +--- .../tests/metrics/test_metrics.py | 12 +-- .../tests/test_jaeger_propagator.py | 8 +- .../src/opentelemetry/test/__init__.py | 55 +++++++++++++ .../tests/__init__.py | 13 +++ .../tests/test_utils.py | 82 +++++++++++++++++++ tox.ini | 4 + 11 files changed, 176 insertions(+), 74 deletions(-) create mode 100644 tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py create mode 100644 tests/opentelemetry-test-utils/tests/__init__.py create mode 100644 tests/opentelemetry-test-utils/tests/test_utils.py diff --git a/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py b/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py index 5b574a9d603..7e180022895 100644 --- a/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py +++ b/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( OTLPLogExporter, @@ -26,9 +25,10 @@ from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( OTLPSpanExporter as HTTPSpanExporter, ) +from opentelemetry.test import TestCase -class TestOTLPExporters(unittest.TestCase): +class TestOTLPExporters(TestCase): def test_constructors(self): for exporter in [ OTLPSpanExporter, @@ -36,9 +36,5 @@ def test_constructors(self): OTLPLogExporter, OTLPMetricExporter, ]: - try: + with self.assertNotRaises(Exception): exporter() - except Exception: # pylint: disable=broad-except - self.fail( - f"Unexpected exception raised when instantiating {exporter.__name__}" - ) diff --git a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py index ae06d963abd..96ba3991819 100644 --- a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py +++ b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py @@ -14,7 +14,6 @@ from math import inf from sys import float_info, version_info -from unittest import TestCase from unittest.mock import patch from pytest import mark @@ -31,6 +30,7 @@ MIN_NORMAL_EXPONENT, MIN_NORMAL_VALUE, ) +from opentelemetry.test import TestCase if version_info >= (3, 9): from math import nextafter @@ -69,12 +69,9 @@ def test_init_called_once(self, mock_init): def test_exponent_mapping_0(self): - try: + with self.assertNotRaises(Exception): ExponentMapping(0) - except Exception as error: - self.fail(f"Unexpected exception raised: {error}") - def test_exponent_mapping_zero(self): exponent_mapping = ExponentMapping(0) diff --git a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py index 9bea75e426b..311f00a0b00 100644 --- a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py +++ b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py @@ -17,7 +17,6 @@ from math import ldexp from sys import float_info from types import MethodType -from unittest import TestCase from unittest.mock import Mock, patch from opentelemetry.sdk.metrics._internal.aggregation import ( @@ -41,6 +40,7 @@ from opentelemetry.sdk.metrics.view import ( ExponentialBucketHistogramAggregation, ) +from opentelemetry.test import TestCase def get_counts(buckets: Buckets) -> int: @@ -793,11 +793,8 @@ def test_boundary_statistics(self): index = mapping.map_to_index(value) - try: + with self.assertNotRaises(Exception): boundary = mapping.get_lower_boundary(index + 1) - except Exception as error: - raise error - self.fail(f"Unexpected exception {error} raised") if boundary < value: above += 1 @@ -836,30 +833,6 @@ def test_aggregate_collect(self): """ Tests a repeated cycle of aggregation and collection. """ - """ - try: - exponential_histogram_aggregation = ( - _ExponentialBucketHistogramAggregation( - Mock(), - Mock(), - ) - ) - - exponential_histogram_aggregation.aggregate(Measurement(2, Mock())) - exponential_histogram_aggregation.collect( - AggregationTemporality.CUMULATIVE, 0 - ) - exponential_histogram_aggregation.aggregate(Measurement(2, Mock())) - exponential_histogram_aggregation.collect( - AggregationTemporality.CUMULATIVE, 0 - ) - exponential_histogram_aggregation.aggregate(Measurement(2, Mock())) - exponential_histogram_aggregation.collect( - AggregationTemporality.CUMULATIVE, 0 - ) - except Exception as error: - self.fail(f"Unexpected exception raised: {error}") - """ exponential_histogram_aggregation = ( _ExponentialBucketHistogramAggregation( Mock(), diff --git a/opentelemetry-sdk/tests/metrics/test_backward_compat.py b/opentelemetry-sdk/tests/metrics/test_backward_compat.py index 7f8fff2acf2..46008554fe6 100644 --- a/opentelemetry-sdk/tests/metrics/test_backward_compat.py +++ b/opentelemetry-sdk/tests/metrics/test_backward_compat.py @@ -26,7 +26,6 @@ """ from typing import Iterable, Sequence -from unittest import TestCase from opentelemetry.metrics import CallbackOptions, Observation from opentelemetry.sdk.metrics import MeterProvider @@ -38,6 +37,7 @@ MetricReader, PeriodicExportingMetricReader, ) +from opentelemetry.test import TestCase # Do not change these classes until after major version 1 @@ -82,30 +82,24 @@ def test_metric_exporter(self): ) # produce some data meter_provider.get_meter("foo").create_counter("mycounter").add(12) - try: + with self.assertNotRaises(Exception): meter_provider.shutdown() - except Exception: - self.fail() def test_metric_reader(self): reader = OrigMetricReader() meter_provider = MeterProvider(metric_readers=[reader]) # produce some data meter_provider.get_meter("foo").create_counter("mycounter").add(12) - try: + with self.assertNotRaises(Exception): meter_provider.shutdown() - except Exception: - self.fail() def test_observable_callback(self): reader = InMemoryMetricReader() meter_provider = MeterProvider(metric_readers=[reader]) # produce some data meter_provider.get_meter("foo").create_counter("mycounter").add(12) - try: + with self.assertNotRaises(Exception): metrics_data = reader.get_metrics_data() - except Exception: - self.fail() self.assertEqual(len(metrics_data.resource_metrics), 1) self.assertEqual( diff --git a/opentelemetry-sdk/tests/metrics/test_import.py b/opentelemetry-sdk/tests/metrics/test_import.py index 70afa91497f..f0302e00de4 100644 --- a/opentelemetry-sdk/tests/metrics/test_import.py +++ b/opentelemetry-sdk/tests/metrics/test_import.py @@ -14,7 +14,7 @@ # pylint: disable=unused-import -from unittest import TestCase +from opentelemetry.test import TestCase class TestImport(TestCase): @@ -23,7 +23,7 @@ def test_import_init(self): Test that the metrics root module has the right symbols """ - try: + with self.assertNotRaises(Exception): from opentelemetry.sdk.metrics import ( # noqa: F401 Counter, Histogram, @@ -34,15 +34,13 @@ def test_import_init(self): ObservableUpDownCounter, UpDownCounter, ) - except Exception as error: - self.fail(f"Unexpected error {error} was raised") def test_import_export(self): """ Test that the metrics export module has the right symbols """ - try: + with self.assertNotRaises(Exception): from opentelemetry.sdk.metrics.export import ( # noqa: F401 AggregationTemporality, ConsoleMetricExporter, @@ -63,15 +61,13 @@ def test_import_export(self): ScopeMetrics, Sum, ) - except Exception as error: - self.fail(f"Unexpected error {error} was raised") def test_import_view(self): """ Test that the metrics view module has the right symbols """ - try: + with self.assertNotRaises(Exception): from opentelemetry.sdk.metrics.view import ( # noqa: F401 Aggregation, DefaultAggregation, @@ -81,5 +77,3 @@ def test_import_view(self): SumAggregation, View, ) - except Exception as error: - self.fail(f"Unexpected error {error} was raised") diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 8373d3dfe0d..0ccadf47cee 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -16,7 +16,6 @@ from logging import WARNING from time import sleep from typing import Iterable, Sequence -from unittest import TestCase from unittest.mock import MagicMock, Mock, patch from opentelemetry.metrics import NoOpMeter @@ -40,6 +39,7 @@ ) from opentelemetry.sdk.metrics.view import SumAggregation, View from opentelemetry.sdk.resources import Resource +from opentelemetry.test import TestCase from opentelemetry.test.concurrency_test import ConcurrencyTestBase, MockFunc @@ -59,7 +59,7 @@ def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: return True -class TestMeterProvider(ConcurrencyTestBase): +class TestMeterProvider(ConcurrencyTestBase, TestCase): def tearDown(self): MeterProvider._all_metric_readers = set() @@ -86,11 +86,9 @@ def test_register_metric_readers(self): metric_reader_0 = PeriodicExportingMetricReader(mock_exporter) metric_reader_1 = PeriodicExportingMetricReader(mock_exporter) - try: + with self.assertNotRaises(Exception): MeterProvider(metric_readers=(metric_reader_0,)) MeterProvider(metric_readers=(metric_reader_1,)) - except Exception as error: - self.fail(f"Unexpected exception {error} raised") with self.assertRaises(Exception): MeterProvider(metric_readers=(metric_reader_0,)) @@ -356,7 +354,7 @@ def setUp(self): self.meter = Meter(Mock(), Mock()) def test_repeated_instrument_names(self): - try: + with self.assertNotRaises(Exception): self.meter.create_counter("counter") self.meter.create_up_down_counter("up_down_counter") self.meter.create_observable_counter( @@ -369,8 +367,6 @@ def test_repeated_instrument_names(self): self.meter.create_observable_up_down_counter( "observable_up_down_counter", callbacks=[Mock()] ) - except Exception as error: - self.fail(f"Unexpected exception raised {error}") for instrument_name in [ "counter", diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py index 81187389a16..a836cdf4033 100644 --- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py +++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest from unittest.mock import Mock import opentelemetry.trace as trace_api @@ -24,6 +23,7 @@ ) from opentelemetry.sdk import trace from opentelemetry.sdk.trace import id_generator +from opentelemetry.test import TestCase FORMAT = jaeger.JaegerPropagator() @@ -57,7 +57,7 @@ def get_context_new_carrier(old_carrier, carrier_baggage=None): return ctx, new_carrier -class TestJaegerPropagator(unittest.TestCase): +class TestJaegerPropagator(TestCase): @classmethod def setUpClass(cls): generator = id_generator.RandomIdGenerator() @@ -236,7 +236,5 @@ def test_non_recording_span_does_not_crash(self): mock_setter = Mock() span = trace_api.NonRecordingSpan(trace_api.SpanContext(1, 1, True)) with trace_api.use_span(span, end_on_exit=True): - try: + with self.assertNotRaises(Exception): FORMAT.inject({}, setter=mock_setter) - except Exception as exc: # pylint: disable=broad-except - self.fail(f"Injecting failed for NonRecordingSpan with {exc}") diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py new file mode 100644 index 00000000000..068ed12e86c --- /dev/null +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py @@ -0,0 +1,55 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# type: ignore + +from traceback import format_tb +from unittest import TestCase + + +class _AssertNotRaisesMixin: + class _AssertNotRaises: + def __init__(self, test_case): + self._test_case = test_case + + def __enter__(self): + return self + + def __exit__(self, type_, value, tb): # pylint: disable=invalid-name + if value is not None and type_ in self._exception_types: + + self._test_case.fail( + "Unexpected exception was raised:\n{}".format( + "\n".join(format_tb(tb)) + ) + ) + + return True + + def __call__(self, exception, *exceptions): + # pylint: disable=attribute-defined-outside-init + self._exception_types = (exception, *exceptions) + return self + + def __init__(self, *args, **kwargs): + + super().__init__(*args, **kwargs) + # pylint: disable=invalid-name + self.assertNotRaises = self._AssertNotRaises(self) + + +class TestCase( + _AssertNotRaisesMixin, TestCase +): # pylint: disable=function-redefined + pass diff --git a/tests/opentelemetry-test-utils/tests/__init__.py b/tests/opentelemetry-test-utils/tests/__init__.py new file mode 100644 index 00000000000..b0a6f428417 --- /dev/null +++ b/tests/opentelemetry-test-utils/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/opentelemetry-test-utils/tests/test_utils.py b/tests/opentelemetry-test-utils/tests/test_utils.py new file mode 100644 index 00000000000..ce97951f86d --- /dev/null +++ b/tests/opentelemetry-test-utils/tests/test_utils.py @@ -0,0 +1,82 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from opentelemetry.test import TestCase + + +class TestAssertNotRaises(TestCase): + def test_no_exception(self): + + try: + + with self.assertNotRaises(Exception): + pass + + except Exception as error: # pylint: disable=broad-except + + self.fail( # pylint: disable=no-member + f"Unexpected exception {error} was raised" + ) + + def test_no_specified_exception_single(self): + + try: + + with self.assertNotRaises(KeyError): + 1 / 0 # pylint: disable=pointless-statement + + except Exception as error: # pylint: disable=broad-except + + self.fail( # pylint: disable=no-member + f"Unexpected exception {error} was raised" + ) + + def test_no_specified_exception_multiple(self): + + try: + + with self.assertNotRaises(KeyError, IndexError): + 1 / 0 # pylint: disable=pointless-statement + + except Exception as error: # pylint: disable=broad-except + + self.fail( # pylint: disable=no-member + f"Unexpected exception {error} was raised" + ) + + def test_exception(self): + + with self.assertRaises(AssertionError): + + with self.assertNotRaises(ZeroDivisionError): + 1 / 0 # pylint: disable=pointless-statement + + def test_missing_exception(self): + + with self.assertRaises(AssertionError) as error: + + with self.assertNotRaises(ZeroDivisionError): + + def raise_zero_division_error(): + raise ZeroDivisionError() + + raise_zero_division_error() + + error_lines = error.exception.args[0].split("\n") + + self.assertEqual( + error_lines[0].strip(), "Unexpected exception was raised:" + ) + self.assertEqual(error_lines[2].strip(), "raise_zero_division_error()") + self.assertEqual(error_lines[5].strip(), "raise ZeroDivisionError()") diff --git a/tox.ini b/tox.ini index 646d3bb58e4..f619b488dc1 100644 --- a/tox.ini +++ b/tox.ini @@ -61,6 +61,9 @@ envlist = py3{7,8,9,10,11}-opentelemetry-propagator-jaeger pypy3-opentelemetry-propagator-jaeger + py3{7,8,9,10,11}-opentelemetry-test-utils + pypy3-opentelemetry-test-utils + lint spellcheck tracecontext @@ -112,6 +115,7 @@ changedir = propagator-b3: propagator/opentelemetry-propagator-b3/tests propagator-jaeger: propagator/opentelemetry-propagator-jaeger/tests + test-utils: tests/opentelemetry-test-utils/tests commands_pre = ; Install without -e to test the actual installation