Let’s look into the next example:
def divide(num_1: float, num_2: float) -> float:
if not isinstance(num_1, (int, float))
or not isinstance(num_2, (int, float)):
raise TypeError("a minimum of certainly one of the inputs "
f"shouldn't be a number: {num_1}, {num_2}")return num_1 / num_2
There are several flows we will test for the function above — blissful flow, a zero denominator, and a non-digit input.
Now, let’s see what such tests would seem like, using pytest
:
from contextlib import nullcontext as does_not_raiseimport pytest
from operations import divide
def test_happy_flow():
with does_not_raise():
assert divide(30, 2.5) shouldn't be None
assert divide(30, 2.5) == 12.0
def test_division_by_zero():
with pytest.raises(ZeroDivisionError) as exc_info:
divide(10.5, 0)
assert exc_info.value.args[0] == "float division by zero"
def test_not_a_digit():
with pytest.raises(TypeError) as exc_info:
divide("a", 10.5)
assert exc_info.value.args[0] ==
"a minimum of certainly one of the inputs shouldn't be a number: a, 10.5"
We may also perform a sanity check to see what happens once we test an invalid flow against the flawed exception type or once we attempt to envision for a raised exception in a blissful flow. In these cases, the tests will fail:
# Each tests below should faildef test_wrong_exception():
with pytest.raises(TypeError) as exc_info:
divide(10.5, 0)
assert exc_info.value.args[0] == "float division by zero"
def test_unexpected_exception_in_happy_flow():
with pytest.raises(Exception):
assert divide(30, 2.5) shouldn't be None
So, why did the tests above fail? The with
context catches the precise variety of exception requested and verifies that the exception type is indeed the one we asked for.
In test_wrong_exception_check
, an exception (ZeroDivisionError
) was thrown, but it surely wasn’t caught by TypeError.
Subsequently, within the stack trace, we’ll see ZeroDivisionError
was thrown and wasn’t caught by the TypeError
context.
In test_redundant_exception_context
our with pytest.raises
context attempted to validate the requested exception type (we provided Exception
on this case) but since no exception was thrown — the test failed with the message Failed: DID NOT RAISE
.
Now, moving on to the subsequent stage, let’s explore how we will make our tests rather more concise and cleaner by utilizing parametrize
.
Parametrize
from contextlib import nullcontext as does_not_raiseimport pytest
from operations import divide
@pytest.mark.parametrize(
"num_1, num_2, expected_result, exception, message",
[
(30, 2.5, 12.0, does_not_raise(), None),
(10.5, 0, None, pytest.raises(ZeroDivisionError),
"float division by zero"),
("a", 10.5, None, pytest.raises(TypeError),
"at least one of the inputs is not a number: a, 10.5")
],
ids=["valid inputs",
"divide by zero",
"not a number input"]
)
def test_division(num_1, num_2, expected_result, exception, message):
with exception as e:
result = divide(num_1, num_2)
assert message is None or message in str(e)
if expected_result shouldn't be None:
assert result == expected_result
The ids
parameter changes the test-case name displayed on the IDE’s test-bar view. Within the screenshot below we will see it in motion: with ids
on the left, and without ids
on the best.
… [Trackback]
[…] Read More Information here to that Topic: bardai.ai/artificial-intelligence/python-exception-testing-clean-and-effective-methods/ […]
… [Trackback]
[…] Find More Information here on that Topic: bardai.ai/artificial-intelligence/python-exception-testing-clean-and-effective-methods/ […]
… [Trackback]
[…] There you will find 83945 more Info to that Topic: bardai.ai/artificial-intelligence/python-exception-testing-clean-and-effective-methods/ […]
… [Trackback]
[…] Read More to that Topic: bardai.ai/artificial-intelligence/python-exception-testing-clean-and-effective-methods/ […]
… [Trackback]
[…] Here you will find 70499 additional Info on that Topic: bardai.ai/artificial-intelligence/python-exception-testing-clean-and-effective-methods/ […]