查询计数:Django基本性能测试

大家好。我们已经为昨天开始的“ Python的Web开发人员”课程的学生准备了另一本有用的材料的翻译





您经常会听到有关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)

上面的代码声明在视图期间,"trucks:list_trucks"应用程序将仅访问数据库6次。但是还有其他事情,请注意,在开始之前,我们首先创建一个新对象Truck,然后说在视图的上下文数据中trucks_list至少有一个对象。在这种测试中,这一点很重要,因为您需要保证不会对空数据集进行测试。重要的是要了解仅实例化一个类是Truck不够的。您需要检查它是否已包含在上下文中。也许您正在过滤卡车清单,所以您的实例很可能Truck不会包含在结果中。

完成上述所有操作后,我们已经取得了重大进展,但是还有另一个重要的步骤很容易忘记。如果我们希望按比例缩放视图,则必须确保性能不会随着返回项目数的增加而降低。最后,如果我们没有多次访问数据库以获取一项,而如果有100个项目,则返回106,则仍然存在性能问题。我们需要恒定数量的数据库调用,这将不取决于返回的项目数。幸运的是,这个问题也非常简单地解决了,我们需要向数据库中添加一个(或几个)元素,然后再次计算点击数。这是测试在最终版本中的外观:

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方法的更多信息

今天就这些了,我希望从这一刻开始,您将在项目开始时就检查应用程序对数据库的查询数量。这不会花费很多时间,但是可以防止应用程序用户数量增加而引起的问题。

顺便说一句,您仍然可以继续前进再见。

All Articles