Всем привет. Мы подготовили перевод еще одного полезного материала для студентов курса «Web-разработчик на Python», который стартовал вчера.
Очень часто можно услышать о методах тестирования, таких как TDD, и о том, как тестировать бизнес-логику приложения. Однако тестирование производительности приложения – это совсем другая задача. Есть много разных способов, но самым общим подходом считается создание среды, в которой можно проводить DDoS-атаку на свое приложение и наблюдать за его поведением. Это очень интересная тема, но это не то, о чем я хочу сегодня поговорить. Сегодня мы рассмотрим более простой тест, тот, который вы можете сделать с использованием юнит-тестов Django по умолчанию: то есть тестирование количество обращений вашего приложения к базе данных.
Протестировать это очень просто, и это именно тот аспект, который может повредить производительности приложения уже на ранних этапах. Этот аспект является самым первым, который подвергается проверке, когда что-то начинает работать медленно. Хорошая новость состоит в том, что есть всего одна вещь, которую вам надо знать, чтобы писать тесты такого рода: метод assertNumQueries, и он довольно прост в использовании. Вот пример:
Вышеприведенный код утверждает, что во время представления (view)
Сделав все перечисленное, мы уже добились значительного прогресса, но есть еще один важный шаг, о котором легко забыть. Если мы хотим, чтобы наши представления могли масштабироваться, мы должны гарантировать, что производительность не снизится по мере роста количества возвращаемых элементов. В конечном итоге у нас все равно появится проблема с производительностью, если мы обратимся к базе данных не 6 раз, чтобы получить один элемент, а 106, если у нас 100 элементов. Нам нужно постоянное число обращений к базе данных, которое не будет зависеть от количества возвращаемых элементов. К счастью, решается эта проблема тоже очень просто, нам нужно добавить еще один (или несколько) элементов в базу данных и снова подсчитать количество обращений. Вот так тест будет выглядеть в окончательном варианте:
Обратите внимание, что мы снова проверяем количество элементов, возвращаемых в контексте, но при втором прогоне мы ожидаем 2 грузовика (
Обеспечение постоянного числа обращений к базе данных при добавлении новых данных является более приоритетным, чем обеспечение небольшого числа обращений в целом.
Последнее, что нужно сделать, это удостовериться, что ваши данные максимально гидратированы. Это значит, что вам необходимо создать связанные данные, которые будут использоваться во время обработки вашего представления. Если вы этого не сделаете, есть риск, что на продакшене ваше приложение будет обращаться к БД чаще, чем в тесте (хотя, возможно, тест оно пройдет успешно). В нашем примере нам нужно было создать
Если число обращений к базе данных перестает быть постоянным после выполнения действий, описанных выше, то поищите больше информации о методах select_related и prefetch_related.
На сегодня все, надеюсь, что с этого момента вы начнете проверять количество запросов вашего приложения к базе данных в самом начале проекта. Это не займет много времени, но предотвратит проблемы, которые могут возникнуть при росте числа пользователей вашего приложения.
Кстати, вы еще можете успеть на курс. До встречи.
Очень часто можно услышать о методах тестирования, таких как TDD, и о том, как тестировать бизнес-логику приложения. Однако тестирование производительности приложения – это совсем другая задача. Есть много разных способов, но самым общим подходом считается создание среды, в которой можно проводить DDoS-атаку на свое приложение и наблюдать за его поведением. Это очень интересная тема, но это не то, о чем я хочу сегодня поговорить. Сегодня мы рассмотрим более простой тест, тот, который вы можете сделать с использованием юнит-тестов Django по умолчанию: то есть тестирование количество обращений вашего приложения к базе данных.
Протестировать это очень просто, и это именно тот аспект, который может повредить производительности приложения уже на ранних этапах. Этот аспект является самым первым, который подвергается проверке, когда что-то начинает работать медленно. Хорошая новость состоит в том, что есть всего одна вещь, которую вам надо знать, чтобы писать тесты такого рода: метод assertNumQueries, и он довольно прост в использовании. Вот пример:
from django.test import TestCase, Client
from django.urls import reverse
from trucks.models import Truck
class TrucksTestCase(TestCase):
def test_list_trucks_view_performance(self):
client = Client()
Truck.objects.create(...)
with self.assertNumQueries(6):
response = client.get(reverse("trucks:list_trucks"))
self.assertEqual(response.context["trucks_list"], 1)
Вышеприведенный код утверждает, что во время представления (view)
"trucks:list_trucks"
приложение обратится к базе данных всего 6 раз. Но здесь есть кое-что еще, обратите внимание, что перед запуском мы сначала создаем новый объект Truck
, а после этого говорим, что в контекстных данных представления trucks_list
есть как минимум один объект. В такого рода тестах это важно, поскольку вам нужна гарантия того, что вы проводите тест не на пустом наборе данных. Важно понимать, что просто создать экземпляр класса Truck
недостаточно. Вам нужно проверить, был ли он включен в контекст. Возможно, вы выполняете фильтрацию списка грузовиков, поэтому есть вероятность, что ваш экземпляр Truck
не будет включен в результат.Сделав все перечисленное, мы уже добились значительного прогресса, но есть еще один важный шаг, о котором легко забыть. Если мы хотим, чтобы наши представления могли масштабироваться, мы должны гарантировать, что производительность не снизится по мере роста количества возвращаемых элементов. В конечном итоге у нас все равно появится проблема с производительностью, если мы обратимся к базе данных не 6 раз, чтобы получить один элемент, а 106, если у нас 100 элементов. Нам нужно постоянное число обращений к базе данных, которое не будет зависеть от количества возвращаемых элементов. К счастью, решается эта проблема тоже очень просто, нам нужно добавить еще один (или несколько) элементов в базу данных и снова подсчитать количество обращений. Вот так тест будет выглядеть в окончательном варианте:
from django.test import TestCase, Client
from django.urls import reverse
from trucks.models import Truck
class TrucksTestCase(TestCase):
def test_list_trucks_view_performance(self):
client = Client()
Truck.objects.create(...)
with self.assertNumQueries(6):
response = client.get(reverse("trucks:list_trucks"))
self.assertEqual(response.context["trucks_list"], 1)
Truck.objects.create(...)
with self.assertNumQueries(6):
response = client.get(reverse("trucks:list_trucks"))
self.assertEqual(response.context["trucks_list"], 2)
Обратите внимание, что мы снова проверяем количество элементов, возвращаемых в контексте, но при втором прогоне мы ожидаем 2 грузовика (
Truck
). Причина такого поведения аналогична первому случаю.Обеспечение постоянного числа обращений к базе данных при добавлении новых данных является более приоритетным, чем обеспечение небольшого числа обращений в целом.
Последнее, что нужно сделать, это удостовериться, что ваши данные максимально гидратированы. Это значит, что вам необходимо создать связанные данные, которые будут использоваться во время обработки вашего представления. Если вы этого не сделаете, есть риск, что на продакшене ваше приложение будет обращаться к БД чаще, чем в тесте (хотя, возможно, тест оно пройдет успешно). В нашем примере нам нужно было создать
TruckDriver
в компанию к нашему Truck
.from trucks.models import Truck, TruckDriver
...
truck = Truck.objects.create(...)
TruckDriver.objects.create(name="Alex", truck=truck)
Если число обращений к базе данных перестает быть постоянным после выполнения действий, описанных выше, то поищите больше информации о методах select_related и prefetch_related.
На сегодня все, надеюсь, что с этого момента вы начнете проверять количество запросов вашего приложения к базе данных в самом начале проекта. Это не займет много времени, но предотвратит проблемы, которые могут возникнуть при росте числа пользователей вашего приложения.
Кстати, вы еще можете успеть на курс. До встречи.