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

前言

自己动手丰衣足食

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

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