Django 如何在单元测试的时候也打印 SQL 语句

1,470 阅读3分钟

前言

自己动手丰衣足食

之前在 思否 提了这个问题,但是没人回答啊,那就只能自己解决了!

Django 如何在单元测试的时候也打印 SQL 语句

不仅仅是思否,stackoverflow 不过都没有人回答

如何输出 SQL 语句

在项目的 settings.py 文件中添加如下内容就可以将一切对数据库的操作翻译为 SQL 语句,但是注意这个模式只有在 settings.py 中的 DEBUG 开关为 True 是才有效!

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

在单元测试中输出 SQL 语句的需求

上面的语句在做单元测试的时候就变得不好使!

根本就不输出 SQL 语句!!!

我一开始以为是上面代码中的 level 设置的不对,应该将 DEBUG 改为 生产,当然,答案并非如此!

找寻答案

为什么会有这个需求呢?其实还是为了调试代码方便嘛,但既然是调试代码,如果涉及数据库的操作却不输出 SQL 语句,就感觉是雾里看花、黑箱操作让人摸不着头脑!!!作为一名优秀的程序员怎么能允许这种事情发生呢?当然不可以!!!

所以我去通读了 Django 官方文档中相关的内容

  • 编写并运行测试

    通过阅读改文档可知:做单元测试的时候会以生产模式运行,这就解释了为什么不输出 SQL 语句,因为我们的日志设置的级别是 DEBUG

    无论配置文件中的 DEBUG 设置值是多少,所有的 Django 测试都以 DEBUG=False 运行。这是为了确保你的代码观察到的输出与生产环境下的输出一致。

  • 日志管理快速入门

    好的,找到了改进方向了,那就 level 改为生产模式就好了嘛!

    但是通过阅读文档发现只有:

    • DEBUG:排查故障时使用的低级别系统信息
    • INFO:一般的系统信息
    • WARNING:描述系统发生了一些小问题的信息
    • ERROR:描述系统发生了大问题的信息
    • CRITICAL:描述系统发生严重问题的信息

    这五种模式,最低就是 DEBUG,根本没有生产模式啊!!!

  • 定义测试运行器

    读完该篇文档,我懂了,之前想要修改 level 的想法是错误的,我们可以也应该通过自定义测试器 的方式来让单元测试也输出 SQL 语句!!!

说干就干!

说干就干

在根目录下面新建一个 testing 文件夹,然后在其中创建一个 testcases.py 文件,在其中写下如下的代码:

from django.test.runner import DiscoverRunner


class DebugDiscoverRunner(DiscoverRunner):
    def __init__(self, *args, **kwargs):
        super().__init__(debug_sql=True, verbosity=2)

这段代码有几个关键点

  • 继承 DiscoverRunner

    这个没什么好说的,默认就是 DiscoverRunner,所以我们继承他

  • debug_sql=True, verbosity=2

    继承它不为别的,就像开启上面的两个参数

testingtestcases.py 这些名字随意,只是我习惯这么放置和取名字,你随意!!!

然后再 settings.py 文件中的任意位置添加下面的代码:

TEST_RUNNER = 'testing.testrunner.DebugDiscoverRunner'

这个时候你只需要再终端输入

python manage.py test

就会调用我们的 DebugDiscoverRunner 来做单元测试,执行 __init__.py 传递 debug_sql=True, verbosity=2 ,就会再控制台源源不断的打印 SQL 语句!!!

一些细节

你会发现 DebugDiscoverRunner__init__.py 传递给 super().__init__(debug_sql=True, verbosity=2) 的参数没有了 *args, **kwargs

,为什么呢?

因为会报错!!!

如下:

vagrant@vagrant:/vagrant$ python manage.py test comments
Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    main()
  File "manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/commands/test.py", line 23, in run_from_argv
    super().run_from_argv(argv)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/commands/test.py", line 52, in handle
    test_runner = TestRunner(**options)
  File "/vagrant/testing/testrunner.py", line 6, in __init__
    super(DebugDiscoverRunner, self).__init__(debug_sql=True, verbosity=2, *args, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'verbosity'

参考文章:Django:DiscoverRunner覆盖引发错误

参考归参考,但是这个人的处理方法很不好

所以我们去掉了 *args, **kwargs