pytest.raises

Introduction

In this article you can get acquainted with using the context manager pytest.raises() in PyTest.

Official documentation

Example

Consider a project with the following original structure

cm ├── tests │ ├── __init__.py │ └── test_phonebook.py ├── phonebook.py └── venv

You can add a name and a number to the phonebook with .add() and look them up with lookup()

# phonebook.py class Phonebook: def __init__(self): self.storage = {} def add(self, name, number): self.storage[name] = number def lookup(self, name): return self.storage[name]

# test_phonebook.py import os import sys ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(ROOT) from cm.phonebook import Phonebook def test_look_up_by_name(): phonebook = Phonebook() phonebook.add("Yuri", "1961") result = phonebook.lookup("Yuri") assert result == "1961"

From the directory that is the parent of cm, run

python -m pytest -vv .\cm\tests\

… collected 1 item cm/tests/test_phonebook.py::test_look_up_by_name PASSED [100%] ============================ 1 passed in 0.04s ==============================

Now let us add a test for looking up a missing entry.

Since there are now two tests, let us move the creation of the phonebook into a fixture .

Since lookup() in Phonebook is implemented as a plain key lookup , we can use this feature to process the results.

import os import sys ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(ROOT) import pytest from cm.phonebook import Phonebook @pytest.fixture def phonebook(): return Phonebook() def test_look_up_by_name(phonebook): phonebook.add("Yuri", "1961") result = phonebook.lookup("Yuri") assert result == "1961" def test_lookup_missing_name(phonebook): with pytest.raises(KeyError): phonebook.lookup("Niel")

python -m pytest -vv .\cm\tests\

… collected 2 items cm/tests/test_phonebook.py::test_look_up_by_name PASSED [ 50%] cm/tests/test_phonebook.py::test_lookup_missing_name PASSED [100%] =============================== 2 passed in 0.02s ===============================

In this test we expect an exception to be raised.

Therefore, if the developers decide to replace self.storage[name] with self.storage.get(name), the test will start producing a False Positive.

def lookup(self, name): return self.storage.get(name)

python -m pytest -vv .\cm\tests\

… collected 2 items cm/tests/test_phonebook.py::test_look_up_by_name PASSED [ 50%] cm/tests/test_phonebook.py::test_lookup_missing_name FAILED [100%] ============================================= FAILURES ============================================= _____________________________________ test_lookup_missing_name _____________________________________ phonebook = <cm.phonebook.Phonebook object at 0x0000026BEE83C1D0> def test_lookup_missing_name(phonebook): > with pytest.raises(KeyError): ^^^^^^^^^^^^^^^^^^^^^^^ E Failed: DID NOT RAISE <class 'KeyError'> cm\tests\test_phonebook.py:22: Failed ===================================== short test summary info ====================================== FAILED cm/tests/test_phonebook.py::test_lookup_missing_name - Failed: DID NOT RAISE <class 'KeyError'> =================================== 1 failed, 1 passed in 0.07s ====================================

The test will have to be replaced with

def test_lookup_missing_name(phonebook): assert phonebook.lookup("Niel") is None

Article author: Arthur Isaev