Nik*_*nyh 6 python testing django unit-testing
如何找到慢django测试?如何定位测试,测试运行器可以"卡住"?你知道任何好的定制django测试跑步者,可以提供更详细的测试性能信息吗?
你可以让Django打印它运行的测试:
./manage.py test -v 3
Run Code Online (Sandbox Code Playgroud)
这将打印测试的名称,运行它,然后打印"确定".所以你可以弄清楚哪个测试很慢.
要识别缓慢的测试,您需要测量每个测试运行所需的持续时间,并定义缓慢对您意味着什么的阈值。
默认情况下,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)
| 归档时间: |
|
| 查看次数: |
1137 次 |
| 最近记录: |