Assertions and pytest.raises
Overview
An **assertion** is a statement that checks if a condition is true. If it is false, the test fails. When we expect our code to fail and raise an error, we use **pytest.raises** to check that the error happens. Here is a worked example checking a student's score: ```python import pytest def get_grade(score): if score < 0: raise ValueError('Score cannot be negative') return 'Pass' if score >= 40 else 'Fail' def test_get_grade_pass(): assert get_grade(50) == 'Pass' def test_get_grade_negative(): with pytest.raises(ValueError): get_grade(-10) ``` The `assert` line checks that 50 returns 'Pass'. The `with pytest.raises` block checks that passing -10 correctly causes a `ValueError`. Now, look at this faded example where one step is blank: ```python import pytest def divide(a, b): if b == 0: raise ZeroDivisionError('Cannot divide by zero') return a / b def test_divide_by_zero(): with pytest._______(ZeroDivisionError): divide(10, 0) ``` To make this test check for the error, we fill in the blank with `raises`.
We must prove our code works when given good data, and that it safely catches bad data. Assertions handle the good, and pytest.raises handles the bad.
Where used: pytest test suites
Why learn this
- Testing error handling paths in your code.
- Proving that functions return the exact expected values.
Code walkthrough
import pytest
def test_catch():
with pytest.raises(KeyError):
my_dict = {'name': 'Rahul'}
bad_key = my_dict['age']
print('Caught the error')
test_catch()
Focus: with pytest.raises(KeyError):
Aha moment
import pytest
def test_no_error():
with pytest.raises(ValueError):
number = 5 + 5
print('Done')
test_no_error()
Prediction: What happens when this test runs?
Common guess: It passes and prints 'Done'.
`pytest.raises` expects an error to happen. Since `5 + 5` does not raise a `ValueError`, the test actually fails because the expected error never occurred.
Common mistakes
- Assigning instead of comparing: Writing `assert result = 5` will cause a syntax error. You must use the double equals sign `assert result == 5` for comparison.
- Putting the function call outside the with block: If you call the function before the `with pytest.raises` block, the error crashes the test immediately. The call must be inside the block.
Recall questions
- Explain assertions and pytest.raises in your own words, as if to a classmate.
- What happens if the condition in an `assert` statement is false?
Understanding checks
Where is the bug in this test?
The division `x = 1 / 0` happens outside the `pytest.raises` block.
The code that raises the error must be indented inside the `with pytest.raises` block so pytest can catch it. Otherwise, it just crashes the test.
What happens when this test runs?
The assertion fails and the print statement never runs.
When an `assert` statement fails, it immediately raises an AssertionError, stopping the rest of the code in that block from running.
Practice tasks
Check the error
Given this code, change the test to correctly check that `IndexError` is raised.
Challenge
Write an assertion and an error check
Write a test function named `test_logic` that first asserts `10 > 5`, and then uses `pytest.raises` to check that `int('abc')` raises a `ValueError`.
Continue learning
Previous: Test discovery and naming conventions
Previous: Custom exception classes