Testing

PyTest

# Allow pdb/ipdb at the pytest
pytest -s tests/

# run pytest with coverage
pytest --cov-report html --cov=. --cov-config coverage.config tests/

# Run pytest coverage for many installed django apps at once
py.test --cov-report html --cov={app1, app2, ...} */tests.py

Example coverage.config file

[run]
source = package_name
omit = .venv/*
       tests/*
       setup.py
       */__init__.py

[report]
# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    pragma: no cover
    # Don't complain if non-runnable code isn't run:
    if 0:
    if __name__ == .__main__.:

—runslow flag

import pytest

def pytest_addoption(parser):
    parser.addoption("--runslow", action="store_true",
                     default=False, help="run slow tests")


def pytest_collection_modifyitems(config, items):
    if config.getoption("--runslow"):
        # --runslow given in cli: do not skip slow tests
        return
    skip_slow = pytest.mark.skip(reason="need --runslow option to run")
    for item in items:
        if "slow" in item.keywords:
            item.add_marker(skip_slow)

Mocks

Imports order matters

from unittest.mock import patch

# if you use
import some.class_name

# in this case you should use mock as
with patch('some.class_name') as mock:
    pass

# but if you import with 'from' to the package 'package_name'
from some import class_name

# you should mock with
with patch('package_name.class_name') as mock:
   pass

Decorators orders

If we use mocks as decorators with some features we should preserve such order

from unittest.mock import patch
import pytest

@pytest.fixture
def my_fixture():
    return

@patch('some.library.second_patch')
@patch('some.library.first_patch')
def test_protocol_prepare(first_patch, second_patch, my_fixture):
    assert True

Patching many instances

Sometimes you need to patch a lot of instances. In this case you can use patch.multiple:

At some script.py

A = 1
B = 2

At tests:

with patch.multiple('script', A=DEFAULT, B=DEFAULT) as patches_dict:
    a_patch = patches_dict['A']
    b_patch = patches_dict['B']

In case you want this in fixture, you may use such approach:

from unittest.mock import patch
import pytest

@pytest.fixture
def multy_patch():
    patcher = patch.multiple('module', var_1=DEFAULT, var_2=DEFAULT)
    started_patcher = patcher.start()
    yield started_patcher
    patcher.stop()

def test_something(multy_patch):
    var_1_patch = multy_patch['var_1']

Classes patching

If you want to patch some method of the tested class itself, use patch.object:

from unittest.mock import patch, PropertyMock

class ClassName:

    def method_name(self):
        pass

    def __hidden_method(self):
        pass

    @property
    def my_property(self):
        pass

# in case of usual method
with patch.object(ClassName, 'method_name') as mock:
    mock.assert_called_with(key=key)

# in case of hidden __method_name
with patch.object(ClassName, '_ClassName__hidden_method') as mock:
    mock.assert_called()

# for properties
with patch('ClassName.my_property', new_callable=PropertyMock) as property_mock:
    property_mock.return_value = 42
    myclass = MyClass()
    mock_last_transaction.assert_called_once_with()

In case you want patch __init__ method and some another method

from unittest.mock import patch

class ClassName:

    def __init__(self, *args, **kwargs):
        # some complicated init
        pass

    def some_important_method(self):
        pass

# first solution without context manager
patcher = patch('module.name.ClassName')
MockedClass = patcher.start()
isntance = MockedClass()
instance.some_important_method.return_value = "your desired value"

# with context managers
with patch('module.name.ClassName') as MockedClass:
    instance = MockedClass.return_value
    instance.some_important_method.return_value = "your desired value"

Interactions with mocks

mock.assert_called()
mock.assert_called_once_with()
mock.assert_called_with(key=key)
assert mock.call_count == 1

Async testing

pytest-asyncio

In case you want make await calls inside your tests you may use pytest-asyncio

For example you have such code that should be tested

async def my_method():
    pass

By default you may test it as

import asyncio

def test_my_method():
    loop = asyncio.get_event_loop()
    result = loop.run_until_complete(my_method())

But you may replace it with

import pytest

@pytest.mark.asyncio
async def test_my_method():
    result = await my_method()

asynctest

When you want to mock some objects that should be awaitable you may use asynctest

class SomeClass:
    def __init__(self, lib):
        self.lib = lib

    async def some_call(self):
        await self.lib()

# just use another imports
from asynctest import CoroutineMock, patch

def test_some_class():
    lib = CoroutineMock()
    cls_ = SomeClass(lib)
    asyncio.get_event_loop().run_until_complete(cls_.some_call())

Hypothesis Testing

Sometimes you may want to test hypothesis. For this you may use such libraries: