Efficient use of pytest fixtures¶

Required boilerplate for using pytest inside notebooks.

In [1]:
# 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() import pytest 
Requirement already satisfied: pytest in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (7.3.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: exceptiongroup>=1.0.0rc8 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (1.1.1) Requirement already satisfied: iniconfig in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest) (2.0.0) 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: 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: 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: pytest>=5.4 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipytest) (7.3.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: iniconfig in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from pytest>=5.4->ipytest) (2.0.0) 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: 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: jedi>=0.16 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.18.2) Requirement already satisfied: appnope in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.1.3) Requirement already satisfied: pickleshare in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.7.5) 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: 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: backcall in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (0.2.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: decorator in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (5.1.1) 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: pexpect>4.3 in /Users/jerrypussinen/.virtualenvs/learn-python/lib/python3.10/site-packages (from ipython->ipytest) (4.8.0) 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: 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: 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: 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: 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 

Parametrizing fixtures¶

Similarly as you can parametrize test functions with pytest.mark.parametrize, you can parametrize fixtures:

In [2]:
PATHS = ["/foo/bar.txt", "/bar/baz.txt"] @pytest.fixture(params=PATHS) def executable(request): return request.param 
In [3]:
%%ipytest -s def test_something_with_executable(executable): print(executable) 
/foo/bar.txt ./bar/baz.txt . 2 passed in 0.01s 

pytest.mark.usefixtures¶

pytest.mark.usefixtures is useful especially when you want to use some fixture in a set of tests but you don't need the return value of the fixture.

In [4]:
%%ipytest -s @pytest.fixture def my_fixture(): print("\nmy_fixture is used") @pytest.fixture def other_fixture(): return "FOO" @pytest.mark.usefixtures('my_fixture') class TestMyStuff: def test_somestuff(self): pass def test_some_other_stuff(self, other_fixture): print(f'here we use also other_fixture which returns: {other_fixture}') 
 my_fixture is used . my_fixture is used here we use also other_fixture which returns: FOO . 2 passed in 0.01s 

pytest built-in fixtures¶

Here are a couple of examples of the useful built-in fixtures, you can view all available fixtures by running pytest --fixtures.

monkeypatch¶

Built-in monkeypatch fixture lets you e.g. set environment variables and set/delete attributes of objects. The use cases are similar as with patching/mocking with unittest.mock.patch/unittest.mock.MagicMock which are part of the Python Standard Library.

Monkeypatching environment variables:

In [5]:
import os def get_env_var_or_none(var_name): return os.environ.get(var_name, None) 
In [6]:
%%ipytest -s def test_get_env_var_or_none_with_valid_env_var(monkeypatch): monkeypatch.setenv('MY_ENV_VAR', 'secret') res = get_env_var_or_none('MY_ENV_VAR') assert res == 'secret' def test_get_env_var_or_none_with_missing_env_var(): res = get_env_var_or_none('NOT_EXISTING') assert res is None 
.. 2 passed in 0.01s 

Monkeypatching attributes:

In [7]:
class SomeClass: some_value = "some value" @staticmethod def tell_the_truth(): print("This is the original truth") 
In [8]:
def fake_truth(): print("This is modified truth") @pytest.fixture def fake_some_class(monkeypatch): monkeypatch.setattr("__main__.SomeClass.some_value", "fake value") monkeypatch.setattr("__main__.SomeClass.tell_the_truth", fake_truth) 
In [9]:
%%ipytest -s def test_some_class(fake_some_class): print(SomeClass.some_value) SomeClass.tell_the_truth() 
fake value This is modified truth . 1 passed in 0.01s 

tmpdir¶

tmpdir fixture provides functionality for creating temporary files and directories.

In [10]:
def word_count_of_txt_file(file_path): with open(file_path) as f: content = f.read() return len(content.split()) 
In [11]:
%%ipytest -s def test_word_count(tmpdir): test_file = tmpdir.join('test.txt') test_file.write('This is example content of seven words') res = word_count_of_txt_file(str(test_file)) # str returns the path assert res == 7 
. 1 passed in 0.01s 

Fixture scope¶

In [12]:
@pytest.fixture(scope="function") def func_fixture(): print("\nfunc") @pytest.fixture(scope="module") def module_fixture(): print("\nmodule") @pytest.fixture(scope="session") def session_fixture(): print("\nsession") 
In [13]:
%%ipytest -s def test_something(func_fixture, module_fixture, session_fixture): pass def test_something_else(func_fixture, module_fixture, session_fixture): pass 
 session module func . func . 2 passed in 0.01s 

Setup-teardown behaviour¶

In [14]:
@pytest.fixture def some_fixture(): print("some_fixture is run now") yield "some magical value" print("\nthis will be run after test execution, you can do e.g. some clean up here") 
In [15]:
%%ipytest -s def test_something(some_fixture): print('running test_something') assert some_fixture == 'some magical value' print('test ends here') 
some_fixture is run now running test_something test ends here . this will be run after test execution, you can do e.g. some clean up here 1 passed in 0.01s 

Using fixtures automatically¶

In [16]:
@pytest.fixture(autouse=True) def my_fixture(): print("\nusing my_fixture") 
In [17]:
%%ipytest -s def test_1(): pass def test_2(): pass 
 using my_fixture . using my_fixture . 2 passed in 0.01s