Тестирование проектов Django
В предыдущем посте мы бегло рассмотрели некоторые приемы тестирования кода на питоне. Все это применимо также и к Django-проектам, безусловно, но есть достаточное количество подводных камней и просто интересных штук, о которых я попробую рассказать.
- тестирование веб-сайтов — это сложно и непонятно
- юнит-тесты в django
- тестовая БД и как с ней бороться
- smoke testing
- покрытие кода (code coverage)
Тестирование веб-сайтов
Самый главный подводный айсберг тестирования Django-проектов заключается в том, что недостаточно написать тесты для питонокода. Разваливается верстка, JavaScript живет своей жизнью, веб-сервер не выдержал нагрузки и превратился в тыкву — все эти вещи выявить при помощи тестирования несколько сложнее, чем отследить неверный результат функции.
Поэтому проверка работоспособности веб-сайта — это обычно сложное явление, состоящее из нескольких независимых наборов тестов, часть которых (проверка внешнего вида в различных браузерах, например) может предполагать участие оператора. При отсутствии отдела QA роль тестировщика нередко возлагают на конечного пользователя, который потом всячески ругается. Так делать неправильно. Полковник Очевидность пост сдал.
Начнем же мы с (относительно) простых и понятных юнит-тестов.
Юнит-тесты в Django
Юнит-тесты в Django живут в модуле django.utils.unittest и являют собой расширение стандартного модуля unittest из поставки python 2.7 (unittest2). Что добавлено:
Тестовый HTTP-клиент. Имитирует работу браузера, может отправлять get- и post-запросы, сохраняет cookies между вызовами.
>>> from django.test.client import Client >>> c = Client() >>> response = c.post('/login/', ) >>> response.status_code 200
С тестовым клиентом связан ряд ограничений. Например, запросить можно только относительный путь, URL вида http://localhost:8000/ не сработает (по понятным причинам).
Расширенный набор проверок. Помимо стандартного набора, класс django.test.TestCase содержит также django-специфичные методы assert* , например:
assertContains(response, text, . ) # проверяет, что в ответе сервера содержится указанный текст; assertTemplateUsed(response, template_name, . ) # проверяет, что при рендеринге страницы использовался указанный шаблон; assertRedirects(response, expected_url, . ) # проверяет, было ли перенаправление;
Тестирование почты. Модуль django.core.mail сохраняет в переменной outbox список всех отправленных посредством send_mail() писем.
Условное исключение тестов. В случае, если выбранная СУБД не поддерживает (или, наоборот, поддерживает) транзакционность, можно исключить заведомо сломанные тесты при помощи декоратора @skipUnlessDBFeature(‘supports_transactions’) или @skipIfDBFeature(‘supports_transactions’) .
Тестирование запускается вот так:
$ ./manage.py test [список приложений]
По умолчанию прогоняются все тесты для всех приложений, перечисленных в INSTALLED_APPS . Пускалка (на языке оригинала — test runner) найдет юнит- и доктесты в файлах models.py и tests.py внутри каждого приложения. Чтобы импортировать доктесты из других модулей, можно использовать следующую запись:
from utils import func_a, func_b __test__ =
Здесь func_* — функция (или другая сущность), docstring которой нас интересует.
Для наблюдателя процесс тестирования выглядит следующим образом:
$ ./manage.py test main Creating test database for alias 'default'. . Ran 10 tests in 0.790s OK Destroying test database for alias 'default'.
Тестовая БД и как с ней бороться
Для запуска тестов Django всегда создает новую БД, чтобы исключить вероятность уничтожения данных в рабочем окружении. Если в settings.py не указано иное, тестовая БД предваряется словом test_. Применимо к MySQL, привилегии обычно задаются как-то так:
GRANT ALL PRIVILEGES ON `project`.* TO 'user'@'localhost'; GRANT ALL PRIVILEGES ON `test_project`.* TO 'user'@'localhost';
Создавать саму БД test_project при этом не нужно.
Хозяйке на заметку. Все работает быстрее, если добавить в конфиг MySQL строку
[mysqld] skip-sync-frm=OFF
Умозрительно, что сразу после создания никаких полезных данных в БД нет. Чтобы не порождать тестовый набор данных внутри каждого теста в отдельности, можно сделать это один раз и сохранить в fixture:
$ ./manage.py dumpdata > app/fixtures/test_data.json
class HelloTestCase(TestCase): fixtures = ['test_data.json', 'moar_data.json']
И еще. Старайтесь использовать для разработки и тестирования ту же СУБД, что и на production-сервере. Это сделает ваш сон на 28% * спокойнее.
* научно доказано, что 87.56% статистики берется с потолка.
Smoke testing
В среде радиолюбителей термин smoke test означает буквально следующее: подключаем к свежесобранной схеме питание и наблюдаем, в каком месте из нее пошел дым. Если дым не пошел, можно приступать к более наукообразной проверке правильности работы схемы.
Описанный подход практикуют также при тестировании приложений. Применимо к Django имеет определенный смысл описывать в tests.py точки входа из URLconf, например, так:
urlpatterns = patterns(None, url(r'^registration/$', registration, name='registration'), url(r'^login/$', . name='login'), url(r'^logout/$', logout_then_login, name='logout'), )
tests.py
from django import test from django.core.urlresolvers import reverse __test__ = >> c = test.Client() >>> c.get(reverse('registration')).status_code 200 >>> c.get(reverse('login')).status_code 200 >>> c.get(reverse('logout')).status_code 302 """>
Безусловно, такая проверка не заменит функционального тестирования регистрации и логина. Полковник Очевидность пост принял.
Покрытие кода (code coverage)
Покрытие кода — это метрика, показывающая, какой объем исходного кода был протестирован относительно всего объема полезного исходного кода в приложении. Низкое покрытие кода указывает на отсутствие тестов.
Хозяйке на заметку-2. Высокое покрытие кода не говорит об отсутствии ошибок (ни в коде, ни в тестах), это вымысел.
Для измерения покрытия кода на питоне существует coverage.py. Гугл помнит много попыток подружить coverage.py и Django, есть даже тикет #4501 (ему четыре года).
И сразу ложка дегтя: с Django 1.3 (и dev-версией) ни одно готовое решение для code coverage, похоже, не работает (поправьте меня, если это не так). Что, впрочем, не помешает нам запустить coverage.py руками.
$ coverage run --source=main,users manage.py test main users $ coverage html # генерация отчета

Перечислим только интересующие нас модули (ключ —source); если не указать, там будет в том числе django, mysqldb и половина стандартной поставки питона.
После этого в папке htmlcov (путь по умолчанию) можно наблюдать детальный отчет по каждой строке кода, покрытие по модулям и суммарное по проекту.
В следующем выпуске: статический анализ как превентивная мера, тестирование верстки и JS, нагрузочное тестирование.
Тестируем на Python: unittest и pytest. Инструкция для начинающих
Python-разработчик Андрей Смирнов рассказал, как написать первые тесты и какие фреймворки выбрать: unittest или pytest.
Меня зовут Андрей Смирнов, я занимаюсь Python-разработкой, автоматизацией технических процессов и преподаю промышленное программирование в Школе программистов МШП.
Не секрет, что разработчики создают программы, которые рано или поздно становятся очень масштабными (если смотреть на количество строчек кода). А с этим приходит и большая ответственность за качество.
Сейчас расскажу, как unittest и pytest помогут найти ошибки в программах и исключить их в будущем.
Итак, тестирование
Каждый, кто писал первые программы (будь то классический «hello, world» или же калькулятор), всегда запускал тесты, чтобы проверить их работу.
Сам факт запуска — самое первое, незримое касание технологии тестирования в вашей жизни. Рассмотрим его как процесс поиска ошибок на чуть более сложной программе.
Например, вам нужно ввести три числа (a, b, c) и найти корни квадратного уравнения. Для решения пишем код:
from math import sqrt def square_eq_solver(a, b, c): result = [] discriminant = b * b - 4 * a * c if discriminant == 0: result.append(-b / (2 * a)) else: result.append((-b + sqrt(discriminant)) / (2 * a)) result.append((-b - sqrt(discriminant)) / (2 * a)) return result def show_result(data): if len(data) > 0: for index, value in enumerate(data): print(f'Корень номер равен ') else: print('Уравнение с заданными параметрами не имеет корней') def main(): a, b, c = map(int, input('Пожалуйста, введите три числа через пробел: ').split()) result = square_eq_solver(a, b, c) show_result(result) if __name__ == '__main__': main()
Сразу оговорюсь: любую задачу, какой бы она ни была краткой, я рассматриваю с позиции «когда-нибудь она вырастет и станет очень объёмной». Поэтому всегда стараюсь разделять программу на различные подпрограммы (ввод/обработка/вывод).
Возможно, вы уже заметили ошибку в коде. Однако иногда она может быть скрыта настолько глубоко, что её просто так не обнаружишь. И в таком случае единственный способ вывести ее на свет — протестировать код. Как это сделать?
— зная алгоритм нахождения корней уравнения, определяем наборы входных данных, которые будут переданы на вход программе;
— зная входные данные, можно вручную просчитать, какой ответ должна дать программа;
— запускаем программу и передаем ей на вход исходные данные;
— получаем от нее ответ и сравниваем с тем, который должен быть получен. Если они совпадают — хорошо, идём к следующему набору данных, если нет, сообщаем об ошибке.
Например, для данной задачи можно подобрать следующие тесты:
- 10x**2 = 0 — единственный корень x=0
- 2x**2 + 5x — 3 = 0 — у такого уравнения два корня (x1 = 0.5, x2=-3)
- 10x**2+2 = 0 — у этого уравнения корней нет
Тесты подобрали, что дальше? Правильно, запускаем:
Тест номер 1 > python.exe example.py Пожалуйста, введите три числа через пробел: 10 0 0 Корень номер 0 равен 0.00 Тест номер 2: > python.exe example.py Пожалуйста, введите три числа через пробел: 2 5 -3 Корень номер 1 равен 0.50 Корень номер 2 равен -3.00 Тест номер 3: > python.exe example.py Пожалуйста, введите три числа через пробел: 10 0 2 Traceback (most recent call last): File "C:PyProjectstprogerexample.py", line 32, in main() File "C:PyProjectstprogerexample.py", line 27, in main result = square_eq_solver(a, b, c) File "C:PyProjectstprogerexample.py", line 11, in square_eq_solver result.append((-b + sqrt(discriminant)) / (2 * a)) ValueError: math domain error
Упс… В третьем тесте произошла ошибка. Как раз та, которую вы могли заметить в исходном коде программы — не обрабатывался случай с нулевым дискриминантом. В итоге, можно подкорректировать код функции так, чтобы этот вариант обрабатывался правильно:
def square_eq_solver(a, b, c): result = [] discriminant = b * b - 4 * a * c if discriminant == 0: result.append(-b / (2 * a)) elif discriminant > 0: #
Запускаем все тесты повторно и они срабатывают нормально.
Но учтите, чтобы повторно проверить программу, потребуется потратить несколько минут и снова проверить все три варианта входных значений. Если таких вариантов будет много, вызывать их вручную будет очень накладно. И здесь на сцену выходит автоматизированное тестирование.
Программа автоматического тестирования запускается на основе заранее заготовленных входных/выходных данных и программы, которая будет их вызывать. По сути, это программа, тестирующая другие программы. И в рамках экосистемы языка Python есть несколько пакетов, позволяющих автоматизировать процесс тестирования.
Unittest и pytest: пишем тесты
Две самые популярные библиотеки — unittest и pytest. Попробуем каждую, чтобы объективно оценить синтаксис.
Начнем с unittest, потому что именно с нее многие знакомятся с миром тестирования. Причина проста: библиотека по умолчанию встроена в стандартную библиотеку языка Python.
Формат кода
По формату написания тестов она сильно напоминает библиотеку JUnit, используемую в языке Java для написания тестов:
- тесты должны быть написаны в классе;
- класс должен быть отнаследован от базового класса unittest.TestCase;
- имена всех функций, являющихся тестами, должны начинаться с ключевого слова test;
- внутри функций должны быть вызовы операторов сравнения (assertX) — именно они будут проверять наши полученные значения на соответствие заявленным.
Пример использования unittest для нашей задачи
import unittest class SquareEqSolverTestCase(unittest.TestCase): def test_no_root(self): res = square_eq_solver(10, 0, 2) self.assertEqual(len(res), 0) def test_single_root(self): res = square_eq_solver(10, 0, 0) self.assertEqual(len(res), 1) self.assertEqual(res, [0]) def test_multiple_root(self): res = square_eq_solver(2, 5, -3) self.assertEqual(len(res), 2) self.assertEqual(res, [0.5, -3])
Запускается данный код следующей командой
python.exe -m unittest example.py
И в результате на экран будет выведено:
> python.exe -m unittest example.py . ------------------------------------------------------------------ Ran 3 tests in 0.001s OK
В случае, если в каком-нибудь из тестов будет обнаружена ошибка, unittest не замедлит о ней сообщить:
> python.exe -m unittest example.py F.. ================================================================== FAIL: test_multiple_root (hello.SquareEqSolverTestCase) ------------------------------------------------------------------ Traceback (most recent call last): File "C:PyProjectstprogerexample.py", line 101, in test_multiple_root self.assertEqual(len(res), 3) AssertionError: 2 != 3 ------------------------------------------------------------------ Ran 3 tests in 0.001s FAILED (failures=1)
Unittest: аргументы “за”
- Является частью стандартной библиотеки языка Python: не нужно устанавливать ничего дополнительно;
- Гибкая структура и условия запуска тестов. Для каждого теста можно назначить теги, в соответствии с которыми будем запускаться либо одна, либо другая группа тестов;
- Быстрая генерация отчетов о проведенном тестировании, как в формате plaintext, так и в формате XML.
Unittest: аргументы “против”
- Для проведения тестирования придётся написать достаточно большое количество кода (по сравнению с другими библиотеками);
- Из-за того, что разработчики вдохновлялись форматом библиотеки JUnit, названия основных функций написаны в стиле camelCase (например setUp и assertEqual);
- В языке python согласно рекомендациям pep8 должен использоваться формат названий snake_case (например set_up и assert_equal).
Pytest
Возможно, наиболее популярный фреймворк с открытым исходным кодом из всех, представленных здесь.
Pytest позволяет провести модульное тестирование (тестирование отдельных компонентов программы), функциональное тестирование (тестирование способности кода удовлетворять бизнес-требования), тестирование API (application programming interface) и многое другое.
Формат кода
Написание тестов здесь намного проще, нежели в unittest. Вам нужно просто написать несколько функций, удовлетворяющих следующим условиям:
- Название функции должно начинаться с ключевого слова test;
- Внутри функции должно проверяться логическое выражение при помощи оператора assert.
Пример использования pytest для нашей задачи:
def test_no_root(): res = square_eq_solver(10, 0, 2) assert len(res) == 0 def test_single_root(): res = square_eq_solver(10, 0, 0) assert len(res) == 1 assert res == [0] def test_multiple_root(): res = square_eq_solver(2, 5, -3) assert len(res) == 3 assert res == [0.5, -3]
Запускается данный код следующей командой
pytest.exe example.py
И в результате на экран будет выведено:
> pytest.exe example.py ======================= test session starts ====================== platform win32 -- Python 3.9.6, pytest-7.1.2, pluggy-1.0.0 rootdir: C:PyProjectstproger collected 3 items example.py . [100%] ======================== 3 passed in 0.03s =======================
В случае ошибки вывод будет несколько больше:
> pytest.exe example.py ======================= test session starts ====================== platform win32 -- Python 3.9.6, pytest-7.1.2, pluggy-1.0.0 rootdir: C:PyProjectstproger collected 3 items example.py ..F [100%] ============================ FAILURES ============================ _______________________ test_multiple_root _______________________ def test_multiple_root(): res = square_eq_solver(2, 5, -3) > assert len(res) == 3 E assert 2 == 3 E + where 2 = len([0.5, -3.0]) example.py:116: AssertionError ===================== short test summary info ==================== FAILED example.py::test_multiple_root - assert 2 == 3 =================== 1 failed, 2 passed in 0.10s ==================
Pytest: аргументы “за”
- Позволяет писать компактные (по сравнению с unittest) наборы тестов;
- В случае возникновения ошибок выводится гораздо больше информации о них;
- Позволяет запускать тесты, написанные для других тестирующих систем;
- Имеет систему плагинов (и сотни этих самых плагинов), расширяющую возможности фреймворка. Примеры таких плагинов: pytest-cov, pytest-django, pytest-bdd;
- Позволяет запускать тесты в параллели (при помощи плагина pytest-xdist).
Pytest: аргументы “против”
- pytest не входит в стандартную библиотеку языка Python. Поэтому его придётся устанавливать отдельно при помощи команды pip install pytest;
- совместимость кода с другими фреймворками отсутствует. Так что, если напишете код под pytest, запустить его при помощи встроенного unittest не получится.
Ну и что лучше?
- Если вам нужно базовое юнит-тестирование и вы знакомы с фреймворками вида xUnit, тогда вам подойдёт unittest.
- Если нужен фреймворк, позволяющий создавать краткие и изящные тесты, реализующие сложную логику проверок, то pytest.
Post Scriptum
Тема контроля качества очень обширна. И даже к написанному мной коду очень легко придраться. Как минимум здесь отсутствует проверка на то, что вводимые данные обязательно должны быть целыми числами. Если ввести любое другое число или даже строку, она обязательно завершится с ошибкой.
Кстати, в этой программе я намеренно оставил ещё одну ошибку (на сей раз уже логическую), связанную с нахождением корня. Напишите в комментариях, с чем она может быть связана, и какой тест поможет её отловить ?
Написание и запуск тестов ¶
Этот документ разделен на два основных раздела. Сначала мы объясним, как писать тесты с помощью Django. Затем мы объясняем, как их выполнять.
Написание тестов ¶
Модульные тесты Django используют unittest стандартный модуль библиотеки Python. Этот модуль определяет тесты в соответствии с подходом на основе классов.
Вот пример класса, наследующего django.test.TestCase , наследующего unittest.TestCase и выполняющего каждый тест в транзакции для обеспечения изоляции:
from django.test import TestCase from myapp.models import Animal class AnimalTestCase(TestCase): def setUp(self): Animal.objects.create(name="lion", sound="roar") Animal.objects.create(name="cat", sound="meow") def test_animals_can_speak(self): """Animals that can speak are correctly identified""" lion = Animal.objects.get(name="lion") cat = Animal.objects.get(name="cat") self.assertEqual(lion.speak(), 'The lion says "roar"') self.assertEqual(cat.speak(), 'The cat says "meow"')
Когда вы запускаете тесты , по умолчанию тестовая утилита находит все тестовые примеры (то есть подклассы unittest.TestCase ) во всех файлах с именами, начинающимися с test , затем автоматически создать набор тестов и запустить его.
Дополнительные сведения unittest см. В документации Python.
Где должны быть тесты?
Шаблон по умолчанию startapp создает файл tests.py в новом приложении. Это хорошо работает, когда есть всего несколько тестов. Но как только набор тестов растет становится лучше реструктурировать файл в модуле Python для того , чтобы отделить тестирование суб-модули , такие как test_models.py , test_views.py , test_forms.py и т.д. Вы можете выбрать наиболее подходящее структурное подразделение.
Если тесты зависят от доступа к базе данных для создания или запроса моделей, тестовые классы должны быть подклассами, django.test.TestCase а не unittest.TestCase .
Используя unittest.TestCase , мы упрощаем тесты, избегая этапа обертывания каждого теста в транзакции и повторной инициализации базы данных. Но если затронутые тесты взаимодействуют с базой данных, их поведение будет варьироваться в зависимости от порядка, в котором они запускаются исполнителем тестов. Это может привести к тому, что некоторые модульные тесты будут выполняться при индивидуальном запуске, но не пройдут, если они являются частью набора.
Запуск тестов ¶
Когда тесты написаны, встает вопрос о их запуске командой test утилиты manage.py проекта:
$ ./manage.py test
Открытие тестов на основе модуля unittest с интегрированным открытия тестов . По умолчанию тесты ищутся в любом файле с именем «test * .py» во всей древовидной структуре из текущего рабочего каталога.
Вы можете выбрать конкретные тесты для запуска, указав «имена тестов» при заказе . Каждое имя теста может быть полным путем Python в указанном синтаксисе к пакету, модулю, подклассу или методу тестирования. Например : ./manage.py test TestCase
# Run all the tests in the animals.tests module $ ./manage.py test animals.tests # Run all the tests found within the 'animals' package $ ./manage.py test animals # Run just one test case $ ./manage.py test animals.tests.AnimalTestCase # Run just one test method $ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak
Также можно указать путь к каталогу для поиска всех тестов в древовидной структуре этого каталога:
$ ./manage.py test animals/
Вы можете указать собственный шаблон имени файла с помощью параметра -p (или --pattern ), если ваши тестовые файлы не соответствуют шаблону test*.py :
$ ./manage.py test --pattern="tests_*.py"
Если вы нажмете Ctrl-C во время выполнения тестов, средство запуска тестов дождется завершения текущего выполняемого теста, а затем плавно выйдет из тестов. При выходе из тестов таким образом средство запуска отображает подробную информацию о сбоях теста, указывает, сколько тестов было выполнено и количество обнаруженных ошибок и сбоев, а затем он удаляет тестовые базы данных как он обычно делает. Таким образом, это Ctrl-C может быть очень полезно, если вы забыли передать опцию --failfast , понимаете, что некоторые тесты неожиданно завершаются неудачно, и вы хотите получить подробную информацию об этих сбоях, не дожидаясь завершения всего набора тестов.
Если вы не хотите ждать окончания текущего теста, вы можете нажать второй раз, Ctrl-C и тест завершится немедленно, но внезапно. Вы не увидите никаких подробностей о тестах, запущенных до этого момента, и все тестовые базы данных, созданные для этого запуска, не будут удалены.
Тесты с активными предупреждениями
Желательно запускать тесты, активировав предупреждения Python . Флаг указывает Python отображать предупреждения об устаревании. Django, как и любая другая библиотека Python, использует эти предупреждения для уведомления о том, что некоторые функции скоро исчезнут. Есть также предостережения, которые могут указать на то, что некоторые области кода не являются в корне неправильными, но могут выиграть от лучшей реализации. python -Wa manage.py test -Wa
Тестовая база данных ¶
Тесты, которым нужна база данных (т.е. тесты с моделями), не используют «настоящую» (производственную) базу данных. Отдельные пустые базы данных создаются специально для тестирования.
Независимо от того, пройдены ли тесты или нет, тестовые базы данных удаляются сразу после выполнения всех тестов.
Вы можете избежать отбрасывания тестовых баз данных, используя опцию . Это помогает сохранять тестовую базу данных между сессиями. Если база данных не существует, она все равно будет создана. Любая миграция также будет применяться, чтобы поддерживать базу данных в актуальном состоянии. test --keepdb
Как объяснялось в предыдущем разделе, если тестовый запуск внезапно прерывается, тестовая база данных не может быть уничтожена. При следующем запуске вас спросят, хотите ли вы повторно использовать или уничтожить базу данных. Используйте опцию, чтобы удалить этот вопрос и автоматически уничтожить базу данных. Это может быть полезно при запуске тестов на сервере непрерывной интеграции, где тесты могут быть остановлены, например, по таймауту. test --noinput
Имена баз данных по умолчанию испытаний создаются предваряя значения каждого NAME в DATABASES с test_ . В SQLite тесты по умолчанию используют базу данных в памяти (то есть база данных создается в памяти без использования файловой системы). Словарь TEST в DATABASES предложениях нескольких параметров , чтобы настроить тестовую базу данных. Например, если вы хотите использовать другое имя базы данных, заполните NAME словарь TEST затронутой базы данных DATABASES .
В PostgreSQL USER также требуется доступ для чтения к встроенной базе данных postgres .
Кроме того, создание отдельной базы данных, тест бегун использует одни и те же параметры базы данных из файла настроек: ENGINE , USER , и HOST т.д. Тестовая база данных создается пользователем USER , поэтому вы должны убедиться, что эта учетная запись пользователя имеет необходимые права для создания новой базы данных в вашей системе.
Для более точного управления кодировкой символов в тестовой базе данных используйте параметр CHARSET TEST. С MySQL также можно использовать параметр COLLATION для управления сопоставлением, используемым тестовой базой данных. См. Документацию по настройкам для получения более подробной информации об этих расширенных настройках.
Если вы используете базу данных SQLite в памяти, общий кеш включен, чтобы вы могли писать тесты с возможностью совместного использования базы данных между потоками.
Доступ к данным производственной базы данных во время выполнения тестов?
Если ваш код пытается получить доступ к базе данных во время компиляции кода, это произойдет до того, как база данных будет настроена, с потенциально неожиданными результатами. Например, если запрос к базе данных запускается в коде уровня модуля и существует фактическая база данных, производственные данные могут помешать тестированию. В любом случае настоятельно рекомендуется не размещать запросы к базе данных, выполняемые при импорте кода , это вопрос переписывания кода, чтобы этого не произошло.
Это также относится к пользовательским реализациям ready() .
Порядок выполнения тестов ¶
Чтобы гарантировать, что весь код TestCase начинается с чистой базы данных, средство запуска тестов Django переупорядочивает тесты следующим образом:
- Все подклассы TestCase выполняются первыми.
- Затем все другие тесты, основанные на Django ( SimpleTestCase включая классы тестов на основе случаев TransactionTestCase ), выполняются без гарантированного определенного порядка сортировки.
- Затем unittest.TestCase запускаются все остальные тесты (включая «doctests»), которые могут изменить базу данных без восстановления ее в начальное состояние.
Новый порядок выполнения тестов может выявить неожиданные зависимости в порядке тестовых примеров. Так обстоит дело с «doctests», которые полагаются на состояние базы данных после TransactionTestCase данного теста ; эти тесты необходимо обновить, чтобы они могли функционировать независимо.
Вы можете изменить порядок выполнения внутри групп, передав эту опцию . Это может помочь гарантировать независимость тестов друг от друга. test --reverse
Эмуляция отката ¶
Любые исходные данные, загруженные при миграциях, доступны только в тестах TestCase , но не в тестах TransactionTestCase и, более того, только с движками, поддерживающими транзакции (наиболее заметным исключением является MyISAM). Это также верно для тестов, основанных на TransactionTestCase таких как LiveServerTestCase и StaticLiveServerTestCase .
Django может перезагрузить эти данные для вас для каждого теста , если вы установите опцию , serialized_rollback чтобы True в теле TestCase или TransactionTestCase , но имейте в виду , что это приведет к замедлению пораженный тестов примерно в 3 раза.
Сторонние приложения или приложения, основанные на механизме MyISAM, должны устанавливать этот атрибут. Однако обычно рекомендуется разрабатывать свои проекты с использованием транзакционной базы данных и тестировать с помощью тестового класса TestCase , и в этом случае этот атрибут не нужен.
Первоначальная сериализация обычно выполняется очень быстро, но если вы хотите исключить определенные приложения из этого процесса (и немного ускорить выполнение теста), вы можете добавить эти приложения в настройку TEST_NON_SERIALIZED_APPS .
Чтобы сериализованные данные не загружались дважды, можно настроить serialized_rollback=True отключение сигнала post_migrate при сбросе тестовой базы данных.
Другие условия испытаний ¶
Все тесты Django запускаются с DEBUG = False, независимо от значения параметра DEBUG в вашем файле настроек. Это гарантирует, что наблюдаемый результат кода соответствует тому, что фактически будет производиться в производственной среде.
Кеши не очищаются после каждого теста, и запуск «manage.py test fooapp» может вставить тестовые данные в кеш онлайн-системы, если вы запускаете тесты в производственной среде, потому что, в отличие от того, что делается с базами данных тесты не используют «тестовый кеш». Это поведение может измениться в будущем.
Что такое тестовый дисплей ¶
Когда вы запускаете тесты, по мере подготовки исполнителя тестов появляется ряд сообщений. Вы можете контролировать уровень детализации этих сообщений с помощью параметра verbosity командной строки:
Creating test database. Creating table myapp_animal Creating table myapp_mineral
Средство выполнения тестов сообщает вам, что он создает тестовую базу данных, как описано в предыдущем разделе.
После создания тестовой базы данных Django запускает фактические тесты. Если все пойдет хорошо, вы увидите что-то вроде этого:
---------------------------------------------------------------------- Ran 22 tests in 0.221s OK
Однако, если какие-либо тесты не прошли, вы увидите полную информацию о неудачных тестах:
====================================================================== FAIL: test_was_published_recently_with_future_poll (polls.tests.PollMethodTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dev/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_poll self.assertIs(future_poll.was_published_recently(), False) AssertionError: True is not False ---------------------------------------------------------------------- Ran 1 test in 0.003s FAILED (failures=1)
Подробное объяснение этого отображения ошибок не входит в задачу этого документа, но это относительно интуитивно понятно. Вы можете обратиться к документации библиотеки Python unittest для получения более подробной информации.
Обратите внимание, что код возврата сценария запуска теста равен 1, независимо от количества неудачных или ошибочных тестов. Если все тесты пройдены, код возврата равен 0. Эта функция полезна, если вы запускаете сценарий тестового запуска из сценария оболочки и вам нужно проверить результат (прошел или не прошел) на этом уровне. ,
Ускорение тестирования ¶
Параллельное выполнение тестов ¶
Пока ваши тесты полностью изолированы, их можно запускать параллельно, чтобы сэкономить время на многоядерном оборудовании. Смотрите . test --parallel
Хеширование паролей ¶
Хеширование пароля по умолчанию намеренно выполняется довольно медленно. Если ваши тесты проверяют подлинность большого количества пользователей, рекомендуется создать файл настроек для конкретного теста и установить PASSWORD_HASHERS для него более быстрый алгоритм хеширования:
PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.MD5PasswordHasher', ]
Не забудьте также включить в PASSWORD_HASHERS любые хэш-алгоритмы, используемые в моментальных снимках, если применимо.
Сохранение тестовой базы ¶
Эта опция сохраняет тестовую базу данных между несколькими запусками. Это игнорирует действия create и destroy и может значительно сократить время выполнения теста. test --keepdb
Чем отличается unittest в django от стандартной python библиотеки unittest

МЕРОПРИЯТИЯ
YADRO X MOSCOW USER GROUP C++ MEETUP
Комментарии
Популярные По порядку
Не удалось загрузить комментарии.
ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ
ООП на Python: концепции, принципы и примеры реализации
Программирование на Python допускает различные методологии, но в его основе лежит объектный подход, поэтому работать в стиле ООП на Python очень просто.
3 самых важных сферы применения Python: возможности языка
Существует множество областей применения Python, но в некоторых он особенно хорош. Разбираемся, что же можно делать на этом ЯП.
Программирование на Python: от новичка до профессионала
Пошаговая инструкция для всех, кто хочет изучить программирование на Python (или программирование вообще), но не знает, куда сделать первый шаг.
