ipytest: running pytest in IPython notebooks

With pytest becoming the de facto standard for writing tests in python, I started to miss its functionality when prototyping code in jupyter notebooks. Thanks to some free time between the years, I decided to add support for running pytest tests inside notebooks to the most recent release of ipytest (see this example).

As described in the original post, my current workflow often involves prototyping the implementation and its tests inside notebooks. They allow for quick iteration cycles and interactive tests of ideas. Overall, I find that notebooks offer a great developer experience, in particular for small components.

On the test side, pytest greatly increases productivity, since it allows for writing clear and succinct test code that is often much cleaner than corresponding unittest code. In my tests, I now routinely rely on fixtures for modularized tests, on xfails for testing not yet implemented features, and parametrize for testing different variants of a single test.

With its recent release, ipytest supports the combination of both jupyter notebooks and pytest. If you'd like to give ipytest a spin, follow theses steps:

  1. Install ipytest and pytest via pip (pip install ipytest pytest)
  2. Set __file__ in your notebook to the notebook name
  3. Import the ipytest magics (import ipytest.magics)
  4. Add %%run_pytest on top of any cell containing tests ( %%run_pytest[clean] to delete any previously defined tests).

A full example can be found below. If you find ipytest useful or have ideas for improvements, I would love some feedback (issue tracker).

The following, somewhat silly, example (full notebook) demonstrates how to use ipytest:

# In[1]:

# set the file name (required)
__file__ = 'Magics.ipynb'

# add ipython magics
import ipytest.magics

import pytest

# In[2]:

%%run_pytest[clean] -qq

def test_sorted():
    assert sorted([5, 1, 4, 2, 3]) == [1, 2, 3, 4, 5]

# In[3]:

%%run_pytest[clean] -qq

# fixtures work, too :)
@pytest.fixture
def dict_list():
    return [
        dict(a='a', b=3),
        dict(a='c', b=1),
        dict(a='b', b=2),
    ]


def test_sorted__key_example_1(dict_list):
    assert sorted(dict_list, key=lambda d: d['a']) == [
        dict(a='a', b=3),
        dict(a='b', b=2),
        dict(a='c', b=1),
    ]


def test_sorted__key_example_2(dict_list):
    assert sorted(dict_list, key=lambda d: d['b']) == [
        dict(a='c', b=1),
        dict(a='b', b=2),
        dict(a='a', b=3),
    ]

# In[4]:

%%run_pytest[clean] -qq

# as does parametrize
@pytest.mark.parametrize('input,expected', [
    ([2, 1], [1, 2]),
    ('zasdqw', list('adqswz')),
])
def test_examples(input, expected):
    actual = sorted(input)
    assert actual == expected