Итак, у нас есть идея потрясающей и всем необходимой батарейки для Django. После того, как мы написали весь код мы готовы релизнуть нашу батарейку в PyPI. Однако перед этим мы должны разобраться с несколькими моментами:‌ Наша батарейка основана на каком-то коде Django, но он может измениться и тогда возникнет несовместимость. Нам необходимо убедится что наша батарейка работает с предыдущими версиями Django. Таким образом нам необходимо протестировать нашу батарейку в нескольких окружениях с разными версиями Django и Python. В качестве примера я взял свою библиотеку django-factory-boy-generator . После того, как мы написали наш функционал, мы должны получить примерно следующую файловую структуру. Теперь нам нужно, конечно же, покрыть наш код тестами, используя, например, стандартную Python библиотеку unittest. Однако тут сразу возникает вопрос, а как быть, если мы хотим тестировать такие чисто джанговские вещи как модели, админку, приложения и т.д.? Таким образом, нам нужно использовать тестовый Django-проект внутри наших тестов. Для начала добавим директорию с тестами в нашу библиотеку. В тех тестах, где мы используем модели и базу данных, нам нужно чтобы наши тестовые классы наследовались от django.test.TestCase. Далее нам необходимо сделать следующее:‌ Создать Django-проект Создать Django-приложение внутри проекта. Добавить файл models.py с моделями. Добавить файл settings.py в котором мы зарегистрируем наши установленные приложения и позволим Django запускать миграции при запуске тестов. Таким образом. структура файлов окажется такой: В файле apps.py находится объект конфига Django-приложения: В settings.py добавим настройки для базы данных, а также список дефолтных приложений, в который включим наше тестовое приложение: Теперь мы можем добавить в models.py модели, которые мы будем использовать в тестах. Так как мы больше не используем unittest.TestCase, нам нужна более разносторонняя библиотека для запуска тестов, это может быть pytest , nose, или даже встроенный джанговский раннер, который можно запускать через python manage.py test . В нашем примере мы будем использовать pytest. Для этого необходимо установить, собственно сам pytest, а также его расширение для Django pytest-django. После установки создадим файл конфигурации pytest.ini. Содержимое pytest.ini : Здесь DJANGO_SETTINGS_MODULE указывает для pytest-django модуль с настройками, которые нужно использовать при выполнении тестов. ‌По умолчанию pytest-django ожидает найти файл manage.py , которого у нас нет, поэтому мы устанавливаем настройку django_find_project=false, которая говорит pytest-django не искать manage.py. Теперь мы можем запустить наши тесты. Вначале мы сказали, что хотим протестировать нашу батарейку на совместимость с разными версиями Django и Python. Если бы мы стали делать это вручную, нам пришлось бы создавать виртуальные окружения с разными версиями зависимостей и запускать в каждом из окружений наши тесты. Вместо этого мы используем библиотеку tox , которая представляет возможность запускать наши тесты в разных окружениях, создаваемых автоматически. Конфигурация tox состоит из двух главных блоков:‌ [tox] - здесь мы определяем окружения для наших тестов. Мы будем использовать следующие: Django 1.11 + Python 3.6, 3.7, 3.8 Django 2.2 + Python 3.6, 3.7, 3.8 Django 3.0 + Python 3.6, 3.7, 3.8 Django 3.1 + Python 3.6, 3.7, 3.8 Django 3.2 + Python 3.6, 3.7, 3.8 Окружение для линтинга нашего кода (Мы будем использовать локальные дев зависимости) [testenv] - здесь перечисляются зависимости и команды, которые будут запускаться в окружениях. deps - список зависимостей, которые нужны в наших окружениях commads - команды которые будут выполнены в окружении Прежде всего создадим файл tox.ini в корне нашего проекта. Для начала определим секцию [tox] и, пока, пустую секцию [testenv]: В envlist мы перечислили наши окружения. Определение окружений состоит из двух частей. Внутри фигурных скобок мы определяем версии питона, с которыми мы хотим тестировать наш проект в перечисленных окружениях, например 37 обозначает Python 3.7. Вне фигурных скобок находится префикс, для перечисленных окружений. Таким образом django22-py{37,36,35} означает что окружение django22 будет создано с тремя разными версиями питона. ‌ Теперь нам нужно добавить наши окружения и команды.‌ Прежде всего, мы определим 2 новые секции. Первая будет содержать все зависимости, которые нам нужны во всех наших окружениях (здесь и далее для примера я указываю зависимости для пакета django-factory-boy-generator ). Во второй определим переменные с зависимостями для разных версий Django. Теперь мы можем определить зависимости в секции [testenv], используя две определенных ранее секции: Кроме того, мы добавили в секцию  переменную commands ,  которая указывает tox, что мы хотим запускать команду pytest во всех наших окружениях. В переменной deps мы говорим tox установить все зависимости, перечисленные в секции  для каждой созданной комбинации. После этого устанавливаются зависимости из секции  в соответствии с указанными окружениями.‌ Последнее что мы добавим в конфиг tox - команды и зависимости для окружения lint. Нам не нужен Django для линтинга нашей батарейки, поэтому мы создадим отдельную ветку в секции  и добавим зависимости и команды для линтинга. В итоге наш файл tox.ini будет выглядеть как-то так: Теперь мы можем запустить тесты в разных окружениях командой tox . Если ваша батарейка выполняет что-либо с моделями, вам нужно убедится, что она будет работать с разными базами данных. На этом этапе нам нужно добавить новую конфигурацию DATABASE в файлsettings.py. В нашем примере нам нужно протестировать работу с PostgreSQL. Кроме того, как вы могли заметить мы добавили настройку DATABASE_ROUTERS. Рассмотрим этот момент подробнее. После того, как мы добавили в Django проект две базы данных, нам нужно каким-то образом указать Django какую из них в каком случае использовать. Для этого нам нужен роутер баз данных. В нашем случае, мы хотим использовать PostgresQL только в тех случаях, когда действия с базой (миграции, чтение, запись и т.п.) происходят с моделями, содержащих специфичные поля для PostgresQL. Роутер выглядит примерно так: Здесь важны методы роутера, которые определяют следует ли выполнять операцию и какую базу данных следует использовать:‌ db_for_read - Использует PostgresQL только в случае если в модели содержаться поля специфичные для PostgresQL, иначе используется база default db_for_write - работает идентично, только для операции записи allow_relation - Использует PostgresQL только если оба объекта между которыми содержится связь имеют поля специфичные для PostgresQL allow_migrate - Этот метод вызывается каждые раз в наших тестах при создании базы данных. Первый раз вызывается с базой default второй с PostgresQL. Здесь мы не разрешаем миграции для базы данных по умолчанию, если эти миграции относятся к моделям с полями PostgreSQL. В противном случае мы им разрешаем. Если вы используете больше баз данных в своем проекте, тогда вам скорее всего придется немного изменить роутер для вашего случая. Осталось только написать наши тесты для моделей, которым нужна другая база данных. Нам нужно явно определить базы данных, которые будут использоваться в этом тестовом примере, иначе мы получим ошибку, сообщающую нам: AssertionError: Database queries to 'postgresql' are not allowed in this test. (Запросы базы данных к 'postgresql' не разрешены в этом тесте.) Вот и все! Далее вам нужно убедится, что ваши тесты проходят во всех окружениях, и батарейка готова к релизу в PyPI. Экс-инженер проектировщик, вайтишник без курсов.