我的 Django 应用程序的一个单元测试失败,显示以下错误信息:
DatabaseError: ORA-00942: table or view does not exist
我希望能够查看导致该错误的实际 SQL 查询语句。你知道如何做到这一点吗?
我的 Django 应用程序的一个单元测试失败,显示以下错误信息:
DatabaseError: ORA-00942: table or view does not exist
我希望能够查看导致该错误的实际 SQL 查询语句。你知道如何做到这一点吗?
另一个选择是使用CaptureQueriesContext
(已在pytest
中测试)。
from django.db import connection
from django.test.utils import CaptureQueriesContext
def test_foo():
with CaptureQueriesContext(connection) as ctx:
# code that runs SQL queries
print(ctx.captured_queries)
参考资料:
如果您想从测试中打印/记录所有 SQL 查询,请尝试像这样子类化 TestCase
:
from django.conf import settings
from django.template import Template, Context
import sys
from django.db import connection
from django.test import TestCase
class LoggingTestCase(TestCase):
@staticmethod
def setUpClass():
# The test runner sets DEBUG to False. Set to True to enable SQL logging.
settings.DEBUG = True
super(LoggingTestCase, LoggingTestCase).setUpClass()
@staticmethod
def tearDownClass():
super(LoggingTestCase, LoggingTestCase).tearDownClass()
time = sum([float(q['time']) for q in connection.queries])
t = Template("{{count}} quer{{count|pluralize:\"y,ies\"}} in {{time}} seconds:\n\n{% for sql in sqllog %}[{{forloop.counter}}] {{sql.time}}s: {{sql.sql|safe}}{% if not forloop.last %}\n\n{% endif %}{% endfor %}")
print >> sys.stderr, t.render(Context({'sqllog': connection.queries, 'count': len(connection.queries), 'time': time}))
# Empty the query list between TestCases.
connection.queries = []
在您的测试中,使用LoggingTestCase
代替TestCase
作为基类。只需记得如果您覆盖了tearDownClass
,则需要调用它。
django.conf.settings
,因此我使用 override_settings
。from django.db import connection, reset_queries
from django.test import override_settings, TransactionTestCase
class TransactionTests(TransactionTestCase):
@override_settings(DEBUG=True)
def test_sql(self):
reset_queries()
try:
# Code that uses the ORM goes here
except Exception as e:
pass
self.assertEqual(connection.queries, [])
TestCase
也可能是合适的,可以查看此 答案 中的差异。
有关 SQL 输出的详细信息,请参阅 Django 文档。
connection.execute_wrapper()
,如下所示:from django.db import connection
def logger(execute, sql, params, many, context):
print(sql, params)
return execute(sql, params, many, context)
class GizmoTest(TestCase):
def test_with_sql_logging(self):
with connection.execute_wrapper(logger):
code_that_uses_database()
测试过Django 2.2。
这不是最干净的解决方案,但是如果您只是想快速进行调试而不安装其他软件包,则可以查找django/db中的execute()方法。
对于Oracle,我猜它在:
django/db/backends/oracle/base.py,然后查找:
def execute
对于PostgreSQL,它位于:
django/db/backends/postgresql_psycopg2/base.py
在CursorWrapper中有一个execute()方法。
两者都捕获IntegrityError和DatabaseError,您可以在那里添加打印语句。
对于想要查看所有SQL查询的人,请在函数调用后立即放置打印语句。
from django.test import TestCase
class TestSomething(TestCase):
@override_settings(DEBUG=True)
def test_something(self):
pass
def tearDown(self):
from django.db import connection
for query in connection.queries:
print(f"✅ {query['sql']}\n")
pytest
和pytest-django
时,只需为其创建一个fixture即可。@pytest.fixture
def debug_queries(db):
""" Because pytest run tests with DEBUG=False
the regular query logging will not work, use this fixture instead
"""
from django.db import connection
from django.test.utils import CaptureQueriesContext
with CaptureQueriesContext(connection):
yield connection
然后在你的测试中
@pytest.mark.django_db
def test__queries(debug_queries):
# run your queries here
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s - %(levelname)s - %(name)s - %(message)s',
},
},
'handlers': {
'default': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'standard',
'stream': 'ext://sys.stdout',
},
},
'loggers': {
'django.db.backends': {
'level': 'DEBUG',
'handlers': ['default'],
'propagate': False,
},
}
}
pytest-django
中的settings
fixture来为测试设置记录器吗?我尝试使用它,但在这种情况下记录日志并没有起作用。 - ogloppytest-django
有一个设置夹具,可以覆盖设置 https://pytest-django.readthedocs.io/en/latest/helpers.html?highlight=fixtures#settings
我在想,也许我可以将其用作“debug_queries”的参数,这样就不需要在test.settings.py
中添加日志配置,只需在需要时使用夹具即可。 - oglopdebug_queries
fixture 中始终将记录器级别设置为 DEBUG,无需破解设置。不要忘记在 fixture 退出时还原级别。 - Eugene K你可以在设置中将控制台级别更改为DEBUG。这适用于Django 1.9。
LOGGING = {
...
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
}
...
}
这里的所有选项都太复杂了/太多事情可能会出错。这适用于 django
>= 1.11+
<= 4.x
[在 main
分支上测试过] (直到将来他们把它弄坏为止..)
它通过完全忽略 settings.DEBUG
并注入 CursorDebugWrapper
来始终使用它,从而记录运行的 SQL。
import inspect
from collections import deque
from contextlib import contextmanager
from unittest import mock
from django.db import connections
from django.db.backends import utils
@contextmanager
def print_queries(using="default"):
"""
[debug] Prints out all the queries in real time
To avoid messing with django's logging and get the SQL working inside
tests where `DEBUG` can be set to `False`, this bypasses all that and
goes straight for the kill.
Example:
class TestSomething(TestCase):
def test_something(self):
with print_queries():
Model.objects.create(a=1, b=2)
def test_something():
with print_queries():
Model.objects.create(a=1, b=2)
"""
def fake_maker(self, cursor):
"""
Normally `make_cursor` uses `CursorWrapper` which does NOT debug.
"""
return utils.CursorDebugWrapper(cursor, self)
class Snitch(deque):
"""
Modified version of `deque` that `print()`s out all the items inserted to it.
"""
def append(self, item):
current_frame = inspect.currentframe().f_back
while True:
info = inspect.getframeinfo(current_frame)
if "/django/" in info.filename:
current_frame = current_frame.f_back
continue
break
print("*", item["sql"], item["time"], f"{info.filename}:{info.lineno}")
return super().append(item)
connection_ = connections[using]
mock_maker = mock.patch("django.db.backends.base.base.BaseDatabaseWrapper.make_cursor", fake_maker)
mock_deque = mock.patch.object(connection_, "queries_log", Snitch(maxlen=connection_.queries_log.maxlen))
with mock_maker, mock_deque:
yield
使用方法如下:
def test_whatever():
...
with print_queries():
Model.objects.create(a=1, b=2) # any query here
...
输出看起来像:
* SELECT .. FROM "table" WHERE ... 0.001 /full/path/file.py:136
* SELECT .. FROM "table" WHERE ... 0.001 /full/path/file.py:245
会告诉你查询在代码中的哪个位置。
debugsqlshell
命令来运行测试吗?这在django-debugtoolbar的文档中没有解释。 - gogognome
raise
关键字即可通过并保持堆栈跟踪完整。 - Andrew Gorcester