Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Entertaining testing with pytest

Entertaining testing with pytest

What seems wrong with python unittest and why pytest can serve as an alternative. Pytest fixtures, parameterizing, tips and tricks, etc.

Python Porto

April 21, 2017
Tweet

More Decks by Python Porto

Other Decks in Programming

Transcript

  1. Testing in Python is a religion • Original sin •

    Absolution through pain and suffering • Mystical experience
  2. Original sin • Original sin — dynamic typing and duck

    typing • As a result, a natural inclination of a Python developer to create little and stupid mistakes exposed as runtime errors
  3. pytest fixtures Naive approach. How I’d do it myself file:

    fixtures.py def get_user(): return User(name='Roman', age=33, ...) file: test_user.py def test_user(): user = get_user() assert user.name == 'Roman'
  4. pytest fixtures Pytest approach file: conftest.py @pytest.fixture def user(): return

    User(name='Roman', age=33, ...) file: test_user.py def test_user(user): assert user.name == 'Roman'
  5. @pytest.fixture def user(): return User(name='Roman', age=33, ...) @pytest.fixture def task(user):

    return Task(user=user, content='...') def test_task(task): assert task.user.name == 'Roman'
  6. Session fixture. Local cache @pytest.yield_fixture(scope='session', autouse=True) def local_cache(): old_settings =

    settings.CACHES settings.CACHES = {'default': {…}} yield settings.CACHES = old_settings
  7. Function fixture. Database transaction rollback @pytest.yield_fixture def tx(): db().start_transaction() yield

    db().rollback() def test_user(user, tx, project, task): # project & task will be removed automatically
  8. Session fixture. Clean redis @pytest.yield_fixture(scope='session') def redis_server(): proc = subp.Popen(['redis-server',

    '--port', 7777], ... ) yield proc proc.terminate() @pytest.fixture def rc(redis_server): client = redis.StrictRedis('redis://127.0.0.1:7777') client.flushall() return client
  9. Fixtures in a separate thread http://bit.ly/test_pool @pytest.fixture(scope='session') def item_gen(): gen

    = Generator(lambda: .) gen.start() return gen @pytest.yield_fixture def item(item_gen, item_rel): item = item_gen.get() yield item item_rel.put(item) @pytest.fixture(scope='session') def item_rel(): rel = Releaser(lambda o: ...) rel.start() return rel
  10. More use cases for fixtures • warnings: turn MySQL warnings

    to errors • mock: initialize mockup objects • freezegun: time management • selenium: run a web driver
  11. What else @pytest.mark.parametrize("input,expected", [ ("3+5", 8), ("2+4", 6), ]) def

    test_eval(input, expected): assert eval(input) == expected
  12. What else def pytest_addoption(parser): parser.addoption("--clean-mysql", action="store_true", default=False) @pytest.fixture(scope='session', autouse=True) def

    clean_mysql(request): if not request.config.getoption(‘--clean-mysql'): return # clean MySQL tables heres
  13. What else • pytest-sugar: beautiful output • pytest-django: integration with

    Django • pytest-xdist: parallel and distributed testing
  14. What else • tox: testing against different versions of Python

    • detox: the same, but in parallel [tox] envlist = py27,py35,py36 [testenv] deps=pytest commands=py.test