Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Нескучное тестирование
с pytest
Роман Иманкулов / @rdotpy / 27 июня 2014
Почему программисты
не любят писать тесты?
Тестирование в Python — это
религия
• Врождённая греховность
• Очищение через страдание
• Мистический опыт
Врождённая греховность
 Врожденные пороки — нестрогая типизация и duck
typing
• Как следствие — природная склонность прог...
Очищение через страдание
Boilerplate Code
    class TestSequenceFunctions(unittest.TestCase):
        def setUp(self):
   ...
Очищение через страдание
Многословные ассерты
    
    self.assertEqual(foo, 1, 
                     'foo is not equal to...
Мистический опыт
Django testing setups & teardowns
Есть ли альтернатива?
pytest
pytest — это не еще один
xUnit фреймворк!
pytest fixtures
То, что отличает pytest
от других фреймворков
pytest fixtures
Наивный подход. Как это бы сделал я сам
file: fixtures.py
    def get_user():
        return User(name='Ro...
pytest fixtures
Подход pytest
file: conftest.py
    @pytest.fixture
    def user():
        return User(name='Roman', age=...
Зависимости между fixtures
@pytest.fixture
def user():
    return User(name='Roman', age=30, ...)       
@pytest.fixture
def task(user):
    return T...
Fixture dependencies. Patching object
@pytest.fixture
def premium(user)
    user.set_premium()
def test_premium(user, prem...
yield_fixture
setup и teardown
в одном флаконе
@pytest.yield_fixture
def user():
    obj = User(name='Roman', age=30, ...)       
    yield obj
    obj.delete()
Fixture scopes
• function scope
• module scope
• session scope
Session fixture. Локальный кеш
@pytest.yield_fixture(scope='session', autouse=True)
def local_cache():
    old_settings = ...
Function fixture.
Database transaction rollback
@pytest.yield_fixture
def tx():
    db().start_transaction()
    yield
   ...
Session fixture. Чистый redis
@pytest.yield_fixture(scope='session')
def redis_server():
    proc = subp.Popen(['redis­ser...
fixtures parametrization
Функция возвращает функцию
@pytest.fixture
def set_lang(user):
    def func(lang_code):    
        user.set_lang(lang_cod...
Странные вещи
Fixtures в отдельном потоке
http://bit.ly/test_pool
@pytest.fixture(scope='session')
def item_gen():
    gen = Generator(l...
Как ещё использовать fixtures
• warnings: turn MySQL warnings to errors
• mock: подготовка mockup объектов
• freezegun: уп...
О чём я ещё не рассказал
def pytest_addoption(parser):
    parser.addoption("­­clean­mysql",
  action="store_true", defaul...
О чём я ещё не рассказал
@pytest.mark.parametrize("input,expected", [
    ("3+5", 8),
    ("2+4", 6),
])
def test_eval(inp...
О чём я ещё не рассказал
• pytest-django: интеграция с Django
• pytest-xdist: параллельные и
распределенные тесты
О чём я ещё не рассказал
• tox: выполнение тестов для разных python
• detox: то же самое, только параллельно
[tox]
envlist...
Спасибо! Вопросы?
Роман Иманкулов / @rdotpy / http://imankulov.name
Нескучное тестирование с pytest
Нескучное тестирование с pytest
Upcoming SlideShare
Loading in …5
×

Нескучное тестирование с pytest

9,329 views

Published on

Написание юнит-тестов большинству представляется занятием скучным и до некоторой степени бесполезным. Мое мнение — это всё оттого, что сама "классическая" схема юнит-тестов подразумевает непродуктивное написание унылого линейного кода.

В докладе я расскажу о том, как с помощью pytest начать писать тесты, которые приятно читать и поддерживать, почему setUp и tearDown — это прошлый век, как с помощью правильной организации fixtures ускорить исполнение тестов, а также какие ещё уловки могут помочь вам в вашей нелегкой борьбе с рутиной.

Published in: Software

Нескучное тестирование с pytest

  1. 1. Нескучное тестирование с pytest Роман Иманкулов / @rdotpy / 27 июня 2014
  2. 2. Почему программисты не любят писать тесты?
  3. 3. Тестирование в Python — это религия • Врождённая греховность • Очищение через страдание • Мистический опыт
  4. 4. Врождённая греховность  Врожденные пороки — нестрогая типизация и duck typing • Как следствие — природная склонность программиста на Python к совершению маленьких и глупых ошибок
  5. 5. Очищение через страдание Boilerplate Code     class TestSequenceFunctions(unittest.TestCase):         def setUp(self):             ...         def tearDown(self):             ...         def testFoo(self):             ...
  6. 6. Очищение через страдание Многословные ассерты          self.assertEqual(foo, 1,                       'foo is not equal to one')
  7. 7. Мистический опыт Django testing setups & teardowns
  8. 8. Есть ли альтернатива?
  9. 9. pytest
  10. 10. pytest — это не еще один xUnit фреймворк!
  11. 11. pytest fixtures То, что отличает pytest от других фреймворков
  12. 12. pytest fixtures Наивный подход. Как это бы сделал я сам file: fixtures.py     def get_user():         return User(name='Roman', age=30, ...)         file: test_user.py     def test_user():         user = get_user()         assert user.name == 'Roman'
  13. 13. pytest fixtures Подход pytest file: conftest.py     @pytest.fixture     def user():         return User(name='Roman', age=30, ...)         file: test_user.py     def test_user(user):         assert user.name == 'Roman'
  14. 14. Зависимости между fixtures
  15. 15. @pytest.fixture def user():     return User(name='Roman', age=30, ...)        @pytest.fixture def task(user):     return Task(user=user, name='...') def test_task(task):     assert task.user.name == 'Roman'
  16. 16. Fixture dependencies. Patching object @pytest.fixture def premium(user)     user.set_premium() def test_premium(user, premium):     assert user.is_premum()
  17. 17. yield_fixture setup и teardown в одном флаконе
  18. 18. @pytest.yield_fixture def user():     obj = User(name='Roman', age=30, ...)            yield obj     obj.delete()
  19. 19. Fixture scopes • function scope • module scope • session scope
  20. 20. Session fixture. Локальный кеш @pytest.yield_fixture(scope='session', autouse=True) def local_cache():     old_settings = settings.CACHES     settings.CACHES = {'default': {…}}     yield     settings.CACHES = old_settings
  21. 21. 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
  22. 22. Session fixture. Чистый 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
  23. 23. fixtures parametrization
  24. 24. Функция возвращает функцию @pytest.fixture def set_lang(user):     def func(lang_code):             user.set_lang(lang_code)     return func def test_languages(user, set_lang):     set_lang('ru')     ...
  25. 25. Странные вещи
  26. 26. Fixtures в отдельном потоке 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
  27. 27. Как ещё использовать fixtures • warnings: turn MySQL warnings to errors • mock: подготовка mockup объектов • freezegun: управление временем • selenium: запуск веб-драйвера
  28. 28. О чём я ещё не рассказал 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
  29. 29. О чём я ещё не рассказал @pytest.mark.parametrize("input,expected", [     ("3+5", 8),     ("2+4", 6), ]) def test_eval(input, expected):     assert eval(input) == expected
  30. 30. О чём я ещё не рассказал • pytest-django: интеграция с Django • pytest-xdist: параллельные и распределенные тесты
  31. 31. О чём я ещё не рассказал • tox: выполнение тестов для разных python • detox: то же самое, только параллельно [tox] envlist = py26,py27 [testenv] deps=pytest   commands=py.test
  32. 32. Спасибо! Вопросы? Роман Иманкулов / @rdotpy / http://imankulov.name

×