Coverage for django_query_capture/test_utils.py: 100%
28 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-11-20 10:20 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-11-20 10:20 +0000
1"""
2Test utility to help you check Duplicate, Similar, and Slow queries in the test
3"""
4import typing
6from contextlib import ContextDecorator, ExitStack
8from django.test import override_settings
9from django.utils.module_loading import import_string
11from django_query_capture import BasePresenter, query_capture
12from django_query_capture.settings import get_config
13from django_query_capture.utils import CaptureStdOutToString
16class AssertInefficientQuery(ContextDecorator):
17 def __init__(
18 self,
19 num: typing.Optional[int] = None,
20 seconds: typing.Optional[int] = None,
21 ignore_patterns: typing.Optional[typing.List[str]] = None,
22 ):
23 """
24 Args:
25 num: `Duplicate`, `Similar` Threshold, The value of the setting is ignored.
26 seconds: `Slow` Threshold, The value of the setting is ignored.
27 ignore_patterns: A list of patterns to ignore IGNORE_SQL_PATTERNS of settings.
28 """
29 self.ignore_patterns = ignore_patterns or get_config()["IGNORE_SQL_PATTERNS"]
30 self.num = num
31 self.seconds = seconds
32 self.presenter_cls: typing.Type[BasePresenter] = import_string(
33 get_config()["PRESENTER"]
34 )
36 def __enter__(self):
37 """
38 Run [query_capture.__enter__][decorators.query_capture] and `override_settings.__enter__`
39 At this time, the `ignore_output=True` is set so that the output of [query_capture][decorators.query_capture] can be ignored.
40 override_settings are used to ignore existing set values within the current context.
41 """
42 self._exit_stack = ExitStack().__enter__()
43 self.query_capture = query_capture(ignore_output=True)
44 config = get_config().copy()
45 config.update(
46 {
47 "PRINT_THRESHOLDS": {
48 **config["PRINT_THRESHOLDS"],
49 "SLOW_MIN_SECOND": self.seconds,
50 "DUPLICATE_MIN_COUNT": self.num,
51 "SIMILAR_MIN_COUNT": self.num,
52 }
53 }
54 )
55 self._exit_stack.enter_context(override_settings(QUERY_CAPTURE=config))
56 self._exit_stack.enter_context(self.query_capture)
57 return self.query_capture
59 def __exit__(self, exc_type, exc_val, exc_tb):
60 """
61 End the context of [query_capture][decorators.query_capture] and override_settings.
62 And if there is an item above the threshold, the test fails and the failed content is printed.
63 """
64 self._exit_stack.close()
66 with CaptureStdOutToString() as stdout:
67 self.presenter_cls(self.query_capture.classifier).print()
68 result = stdout.getvalue()
69 if self.query_capture.classifier["has_over_threshold"]:
70 raise AssertionError(result)