# Let's make sure pytest and ipytest packages are installed
# ipytest is required for running pytest inside Jupyter notebooks
import sys
!{sys.executable} -m pip install pytest
!{sys.executable} -m pip install ipytest
# These are needed for running pytest inside Jupyter notebooks
import ipytest
ipytest.autoconfig()
Requirement already satisfied: pytest in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (7.3.1) Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (1.1.1) Requirement already satisfied: tomli>=1.0.0 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (2.0.1) Requirement already satisfied: pluggy<2.0,>=0.12 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (1.0.0) Requirement already satisfied: iniconfig in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (2.0.0) Requirement already satisfied: packaging in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (23.1) [notice] A new release of pip available: 22.3.1 -> 23.1.1 [notice] To update, run: pip install --upgrade pip Requirement already satisfied: ipytest in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (0.13.1) Requirement already satisfied: pytest>=5.4 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipytest) (7.3.1) Requirement already satisfied: ipython in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipytest) (8.12.0) Requirement already satisfied: packaging in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipytest) (23.1) Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest>=5.4->ipytest) (1.1.1) Requirement already satisfied: tomli>=1.0.0 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest>=5.4->ipytest) (2.0.1) Requirement already satisfied: pluggy<2.0,>=0.12 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest>=5.4->ipytest) (1.0.0) Requirement already satisfied: iniconfig in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest>=5.4->ipytest) (2.0.0) Requirement already satisfied: pexpect>4.3 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (4.8.0) Requirement already satisfied: pygments>=2.4.0 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (2.15.0) Requirement already satisfied: jedi>=0.16 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.18.2) Requirement already satisfied: prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (3.0.38) Requirement already satisfied: traitlets>=5 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (5.9.0) Requirement already satisfied: pickleshare in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.7.5) Requirement already satisfied: backcall in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.2.0) Requirement already satisfied: stack-data in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.6.2) Requirement already satisfied: matplotlib-inline in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.1.6) Requirement already satisfied: decorator in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (5.1.1) Requirement already satisfied: appnope in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.1.3) Requirement already satisfied: parso<0.9.0,>=0.8.0 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from jedi>=0.16->ipython->ipytest) (0.8.3) Requirement already satisfied: ptyprocess>=0.5 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pexpect>4.3->ipython->ipytest) (0.7.0) Requirement already satisfied: wcwidth in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30->ipython->ipytest) (0.2.6) Requirement already satisfied: asttokens>=2.1.0 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from stack-data->ipython->ipytest) (2.2.1) Requirement already satisfied: executing>=1.2.0 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from stack-data->ipython->ipytest) (1.2.0) Requirement already satisfied: pure-eval in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from stack-data->ipython->ipytest) (0.2.2) Requirement already satisfied: six in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from asttokens>=2.1.0->stack-data->ipython->ipytest) (1.16.0) [notice] A new release of pip available: 22.3.1 -> 23.1.1 [notice] To update, run: pip install --upgrade pip
@pytest.fixture
¶Let's consider we have an implemention of Person
class which we want to test.
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def as_dict(self):
return {"name": self.full_name, "age": self.age}
def increase_age(self, years):
if years < 0:
raise ValueError("Can not make people younger :(")
self.age += years
You can easily create resusable testing code by using pytest fixtures. If you introduce your fixtures inside conftest.py, the fixtures are available for all your test cases. In general, the location of conftest.py is at the root of your tests directory.
import pytest
@pytest.fixture()
def default_person():
person = Person(first_name="John", last_name="Doe", age=82)
return person
Then you can utilize default_person
fixture in the actual test cases.
%%ipytest
def test_full_name(default_person): # Note: we use fixture as an argument of the test case
result = default_person.full_name
assert result == 'John Doe'
def test_as_dict(default_person):
expected = {'name': 'John Doe', 'age': 82}
result = default_person.as_dict
assert result == expected
def test_increase_age(default_person):
default_person.increase_age(1)
assert default_person.age == 83
default_person.increase_age(10)
assert default_person.age == 93
def test_increase_age_with_negative_number(default_person):
with pytest.raises(ValueError):
default_person.increase_age(-1)
.... [100%] 4 passed in 0.01s
By using a fixture, we could use the same default_person
for all our four test cases!
In the test_increase_age_with_negative_number
we used pytest.raises
to verify that an exception is raised.
@pytest.mark.parametrize
¶Sometimes you want to test the same functionality with multiple different inputs. pytest.mark.parametrize
is your solution for defining multiple different inputs with expected outputs. Let's consider the following implementation of replace_names
function.
def replace_names(original_str, new_name):
"""Replaces names (uppercase words) of original_str by new_name"""
words = original_str.split()
manipulated_words = [new_name if w.istitle() else w for w in words]
return " ".join(manipulated_words)
We can test the replace_names
function with multiple inputs by using pytest.mark.parametrize
.
%%ipytest
@pytest.mark.parametrize("original,new_name,expected", [
('this is Lisa', 'John Doe', 'this is John Doe'),
('how about Frank and Amy', 'John', 'how about John and John'),
('no names here', 'John Doe', 'no names here'),
])
def test_replace_names(original, new_name, expected):
result = replace_names(original, new_name)
assert result == expected
... [100%] 3 passed in 0.01s