Slide 1

Slide 1 text

Современный туллинг тестов в Python Полюби писать тесты!

Slide 2

Slide 2 text

Кто я такой? • Работаю в Kion

Slide 3

Slide 3 text

Кто я такой? • Работаю в Kion • Python разработчик

Slide 4

Slide 4 text

Кто я такой? • Работаю в Kion • Python разработчик • Оптимизирую сервисы и делаю их надежнее

Slide 5

Slide 5 text

Кто я такой? • Работаю в Kion • Python разработчик • Оптимизирую сервисы и делаю их надежнее • Таксовод

Slide 6

Slide 6 text

Зачем?

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

О чём будет доклад • Стабильность тестов

Slide 11

Slide 11 text

О чём будет доклад • Стабильность тестов • Скорость тестов

Slide 12

Slide 12 text

О чём будет доклад • Стабильность тестов • Скорость тестов • Не придумываем костыли

Slide 13

Slide 13 text

О чём будет доклад • Стабильность тестов • Скорость тестов • Убираем бойлерплейт • Генерируем данные

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Стабильность и надежность тестов Почему тесты должны быть стабильными?

Slide 16

Slide 16 text

Критерии надежности • Тест выдаёт одинаковый результат при любом количестве перезапусков

Slide 17

Slide 17 text

Критерии надежности • Тест выдаёт одинаковый результат при любом количестве перезапусков • Тесты проходят и локально, и в ci, и где бы вы их не запустили

Slide 18

Slide 18 text

Критерии надежности • Тест выдаёт одинаковый результат при любом количестве перезапусков • Тесты проходят и локально, и в ci, и где бы вы их не запустили • Можно запустить любое количество тестов и в любом порядке

Slide 19

Slide 19 text

Критерии надежности • Тест выдаёт одинаковый результат при любом количестве перезапусков • Тесты проходят и локально, и в ci, и где бы вы их не запустили • Можно запустить любое количество тестов и в любом порядке • Вы понимаете, зачем этот тест нужен

Slide 20

Slide 20 text

Проблема: нарушение изоляции тестов

Slide 21

Slide 21 text

movie_id = None def test_create_movie() -> None: movie = create_movie() try: UUID(movie.id) except ValueError: raise AssertionError('movie id is not UUID') global movie_id movie_id = movie.id def test_load_movie(): movie = load_movie(movie_id) assert movie_id == movie.id

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Проблема: неконтролируемая случайность данных

Slide 25

Slide 25 text

class Movie(BaseModel): title: str = Field(max_length=120) def readable_title(self) -> str: return textwrap.wrap(self.title, 30)[0] + '...' def test_movie_title_in_film_page(text_title: str) -> None: movie = Movie(title=text_title) assert movie.title in film_page

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

pytest-randomly

Slide 29

Slide 29 text

Полезные ссылки ● Статья от pytest о том, что такое flaky tests ● GitHub pytest-randomly ● Книга Speed Up Your Django Tests

Slide 30

Slide 30 text

Проблема: время всегда неумолимо идёт вперёд

Slide 31

Slide 31 text

from datetime import datetime def get_greeting() -> str: current_time = datetime.now() if current_time.hour < 11: return "Good morning!" if 11 <= current_time.hour < 18: return "Good afternoon!" return "Good evening!"

Slide 32

Slide 32 text

def test_get_greeting() -> None: greeting = get_greeting() assert greeting in ["Good morning!", "Good afternoon!", "Good evening!"]

Slide 33

Slide 33 text

def test_get_greeting() -> None: greeting = get_greeting() assert greeting in ["Good morning!", "Good afternoon!", "Good evening!"]

Slide 34

Slide 34 text

pytest-freezer

Slide 35

Slide 35 text

def test_good_afternoon(freezer) -> None: freezer.move_to("2020-10-17 12:00:00") greeting = get_greeting() assert greeting == "Good afternoon!" def test_good_evening(freezer) -> None: freezer.move_to("2020-10-17 19:13:00") greeting = get_greeting() assert greeting == "Good evening!"

Slide 36

Slide 36 text

Полезные ссылки ● Небольшой туториал по freezegun и pytest-freezegun ● Тесты в проекте tough-dev-school (очень советую)

Slide 37

Slide 37 text

Проблема: не всегда сеть так стабильна как мы этого хотим

Slide 38

Slide 38 text

pytest-retry

Slide 39

Slide 39 text

import pytest @pytest.mark.flaky(retries=3, delay=1) def test_unreliable_service(movie_id: str): assert fetch_movie(movie_id) == Movie( id=movie_id, title='<...>', play_url=f'https://movie.cdn.company.ru/{movie_id} ' )

Slide 40

Slide 40 text

Моки

Slide 41

Slide 41 text

Полезные ссылки • Довольно полное описание в самом репозитории • How-to гайд от pytest о том, как перезапускать только упавшие тесты

Slide 42

Slide 42 text

Проблема: изменение переменных окружения

Slide 43

Slide 43 text

import os def test_modified_env() -> None: os.environ['CUSTOM_ENV'] = 1

Slide 44

Slide 44 text

pytest-modified-env

Slide 45

Slide 45 text

import os from pytest import MonkeyPatch def test_modified_env(monkeypatch: MonkeyPatch) -> None: monkeypatch.setenv('CUSTOM_ENV', 1)

Slide 46

Slide 46 text

pytest-env

Slide 47

Slide 47 text

Полезные ссылки ● Как использовать monkeypatch ● Статья о том, как использовать pytest-env и его аналоги

Slide 48

Slide 48 text

Проблема: системные зависимости

Slide 49

Slide 49 text

Docker

Slide 50

Slide 50 text

Скорость тестов Зачем тестам нужна скорость?

Slide 51

Slide 51 text

Почему тесты тормозят ● Долгие действия в тестах ● Вы красавчик и у вас ОЧЕНЬ много тестов ● Долгие действия в приложении

Slide 52

Slide 52 text

Поиск долгих действий # Profiling test execution duration To get a list of the slowest 10 test durations over 1.0s long: `pytest --durations=10 --durations-min=1.0` By default, pytest will not show test durations that are too small (<0.005s) unless -vv is passed on the command-line.

Slide 53

Slide 53 text

pytest-skip-slow

Slide 54

Slide 54 text

pytest-timeout

Slide 55

Slide 55 text

Полезные ссылки ● Неплохой туториал по тому, когда и зачем использовать timeout для тестов ● Ещё один

Slide 56

Slide 56 text

pytest-xdist

Slide 57

Slide 57 text

Полезные ссылки ● Известные проблемы в pytest-xdist ● Хорошая статья для того, чтобы начать использовать pytest- xdist

Slide 58

Slide 58 text

Docker-compose и контроль за сетью

Slide 59

Slide 59 text

Полезные ссылки ● Разница между юнит тестированием и интеграционным ● Книга «Принципы юнит-тестирования» Владимир Хориков

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

Генерация тестовых данных

Slide 62

Slide 62 text

Генерация данных для Django моделей

Slide 63

Slide 63 text

Hypothesis

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

НЕ Плагины • mypy • ruff • wemake-python-styleguide

Slide 72

Slide 72 text

Если хотите попробовать.. • Существующие хуки • Как писать свои плагины • Как писать свои хуки

Slide 73

Slide 73 text

Выводы ● Не полагайтесь на случайность, контролируйте её!

Slide 74

Slide 74 text

Выводы ● Не полагайтесь на случайность, контролируйте её! ● Изолируйте ваши тесты друг от друга и от глобального состояния

Slide 75

Slide 75 text

Выводы ● Не полагайтесь на случайность, контролируйте её! ● Изолируйте ваши тесты друг от друга и от глобального состояния ● Правильно подбирайте инструмент под уровень ваших тестов

Slide 76

Slide 76 text

Выводы ● Не полагайтесь на случайность, контролируйте её! ● Изолируйте ваши тесты друг от друга и от глобального состояния ● Правильно подбирайте инструмент под уровень ваших тестов ● Следите за скоростью ваших тестов с самого начала, чтобы потом не было больно

Slide 77

Slide 77 text

Выводы ● Не полагайтесь на случайность, контролируйте её! ● Изолируйте ваши тесты друг от друга и от глобального состояния ● Правильно подбирайте инструмент под уровень ваших тестов ● Следите за скоростью ваших тестов с самого начала, чтобы потом не было больно ● Пробуйте тестировать не только логику через данные, но и данные через логику!

Slide 78

Slide 78 text

Вопросы