Django的.如何定位慢速测试?

Nik*_*nyh 6 python testing django unit-testing

如何找到慢django测试?如何定位测试,测试运行器可以"卡住"?你知道任何好的定制django测试跑步者,可以提供更详细的测试性能信息吗?

use*_*876 9

你可以让Django打印它运行的测试:

./manage.py test -v 3
Run Code Online (Sandbox Code Playgroud)

这将打印测试的名称,运行它,然后打印"确定".所以你可以弄清楚哪个测试很慢.


ces*_*sor 6

要识别缓慢的测试,您需要测量每个测试运行所需的持续时间,并定义缓慢对您意味着什么的阈值。

默认情况下,Django 的测试运行器不显示详细的计时信息。您可以使用替代测试运行程序,其中一些将显示测试运行的计时数据。

然而,您可以轻松地自己编写,因为 Django 使用 Python 的单元测试工具,这些工具有详细的文档记录并且可以扩展。

可以在此处的一个文件中找到以下代码: https ://gist.github.com/cessor/44446799736bbe801dc5565b28bfe58b

为了运行测试,Django 使用 a DiscoverRunner(它包装了 Python 的TestRunner)。运行程序将按照惯例从您的项目结构中发现测试用例。案件被收集TestSuite并执行。对于每个测试方法,TestResult都会创建一个测试方法,然后用于将结果传达给开发人员。

要测量测试运行需要多长时间,您可以将 替换TestResult为自定义类:

import unittest
from django.test.runner import DiscoverRunner

class StopwatchTestResult(unittest.TextTestResult):
    ...

class StopwatchTestRunner(DiscoverRunner):
    def get_resultclass(self):
        return StopwatchTestResult
Run Code Online (Sandbox Code Playgroud)

在此示例中,上面的代码放置在example名为 的模块中调用的 Django 应用程序中testrunner.py。要执行它,请像这样调用 Django 的测试命令:

$ python manage.py test --testrunner=example.testrunner.StopwatchTestRunner -v 2
Run Code Online (Sandbox Code Playgroud)

可以StopwatchTestResult注册定时数据:

import time
...
class StopwatchTestResult(unittest.TextTestResult):
    """
    Times test runs and formats the result
    """

    # Collection shared between all result instaces to calculate statistics
    timings = {}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.start = 0
        self.stop = 0
        self.elapsed = 0

    def startTest(self, test):
        self.start = time.time()
        super().startTest(test)

    def stopTest(self, test):
        super().stopTest(test)
        self.stop = time.time()
        self.elapsed = self.stop - self.start
        self.timings[test] = self.elapsed

    def getDescription(self, test):
        """
        Format test result with timing info
        e.g. `test_add (module) [0.1s]`
        """
        description = super().getDescription(test)
        return f'{description} [{self.elapsed:0.4f}s]'
Run Code Online (Sandbox Code Playgroud)

对于每个测试方法,TestResult.startTest都会调用并注册一个时间戳。每次测试运行的持续时间按 计算TestResult.stopTest

当详细程度增加时,将打印描述,这允许您格式化计时数据:

test_student_can_retract_their_request_commit (person.tests.test_view_new.test_view_person_new.NewViewTest) [0.0080s] ... ok
test_student_can_see_their_request_status (person.tests.test_view_new.test_view_person_new.NewViewTest) [0.0121s] ... ok
test_student_can_submit_request (person.tests.test_view_new.test_view_person_new.NewViewTest) [0.0101s] ... ok
Run Code Online (Sandbox Code Playgroud)

要识别缓慢的测试,您可以观察解决方案或使用系统方法:

class StopwatchTestRunner(DiscoverRunner):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._stats = kwargs['stats']

    @classmethod
    def add_arguments(cls, parser):
        DiscoverRunner.add_arguments(parser)
        parser.add_argument(
            "--stats",
            action="store_true",
            help="Print timing statistics",
        )

    def run_tests(self, test_labels, extra_tests=None, **kwargs):
        super().run_tests(test_labels, extra_tests, **kwargs)
        if self._stats:
            StopwatchTestResult.print_stats()

    def get_resultclass(self):
        ...
Run Code Online (Sandbox Code Playgroud)

此测试运行程序向测试命令添加了一个新选项。--stats当设置该选项时,该命令将打印附加统计信息:

test_student_can_retract_their_request_commit (person.tests.test_view_new.test_view_person_new.NewViewTest) [0.0080s] ... ok
test_student_can_see_their_request_status (person.tests.test_view_new.test_view_person_new.NewViewTest) [0.0121s] ... ok
test_student_can_submit_request (person.tests.test_view_new.test_view_person_new.NewViewTest) [0.0101s] ... ok
Run Code Online (Sandbox Code Playgroud)

实际的计算和输出是在 中执行的 StopwatchTestResult.print_stats()。它作为类方法实现,因为它引用属于所有 TestResults 的数据:

class StopwatchTestRunner(DiscoverRunner):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._stats = kwargs['stats']

    @classmethod
    def add_arguments(cls, parser):
        DiscoverRunner.add_arguments(parser)
        parser.add_argument(
            "--stats",
            action="store_true",
            help="Print timing statistics",
        )

    def run_tests(self, test_labels, extra_tests=None, **kwargs):
        super().run_tests(test_labels, extra_tests, **kwargs)
        if self._stats:
            StopwatchTestResult.print_stats()

    def get_resultclass(self):
        ...
Run Code Online (Sandbox Code Playgroud)

要识别缓慢的测试,您可以分析获得的计时数据是否存在异常值。通常,人们会计算平均值和标准差,并将异常值定义为超出特定阈值的所有值,例如与平均值的 +1.5 个标准差。

但是,在收集计时数据时,分布会出现偏差,因为响应速度不能快于 0 秒,而缓慢的异常值会将分布的均值拖到右侧,形成长尾。在这些情况下,平均值和标准差可能难以解释,因此代码使用不同的方法来识别异常值。

使用statistics.quantilesPython 的统计模块(需要 Python 3.8)。这产生了分布的 25%、50% 和 75% 边界。为了识别异常值,您可以将阈值定义为:

slow_threshold = q3 + 1.5 * iqr
Run Code Online (Sandbox Code Playgroud)

您可能需要根据您的目的微调此公式。IQR 是分布离散度的参考(四分位数范围,q3 - q1)。您可以从中位数开始计算阈值,而不是使用 q3。您可以减少或增加常数因子,这会使异常值检测更加敏感或不那么敏感。

总之,这会产生以下输出:

$ python manage.py test --testrunner=example.testrunner.StopwatchTestRunner -v 2 --stats 
Run Code Online (Sandbox Code Playgroud)


jro*_*jro 3

你可以试试鼻子。有大量关于将其与 Django 一起安装的教程。要获得测试时间的高级概述,请查看匹诺曹鼻子延长件,特别是秒表延长件。