从 PyCharm 社区版中的鼠标右键单击上下文菜单运行/调试 Django 应用程序单元测试?

Posted

技术标签:

【中文标题】从 PyCharm 社区版中的鼠标右键单击上下文菜单运行/调试 Django 应用程序单元测试?【英文标题】:Run / Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? 【发布时间】:2017-08-16 19:11:50 【问题描述】:

我必须强调 PyCharm Community,它没有任何 Django 集成v 2016.3.2 提问时间)。

我已经谷歌搜索了我的问题,但(令人惊讶的是)我没有得到任何答案,(当然我不排除可能有一些问题的可能性,但是我只是错过了他们)。

问题很简单:在 PyCharm 中,只需单击鼠标右键(从上下文菜单),如下图所示:

不幸的是,这会产生异常:

Traceback (most recent call last):
    File "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py", line 254, in <module>
        main()
    File "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py", line 232, in main
        module = loadSource(a[0])
    File "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py", line 65, in loadSource
        module = imp.load_source(moduleName, fileName)
    File "E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py", line 7, in <module>
        from polls.models import Question
    File "E:\Work\Dev\Django\Tutorials\proj0\src\polls\models.py", line 9, in <module>
        class Question(models.Model):
    File "E:\Work\Dev\Django\Tutorials\proj0\src\polls\models.py", line 10, in Question
        question_text = models.CharField(max_length=200)
    File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\db\models\fields\__init__.py", line 1043, in __init__
        super(CharField, self).__init__(*args, **kwargs)
    File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\db\models\fields\__init__.py", line 166, in __init__
        self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
    File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\conf\__init__.py", line 53, in __getattr__
        self._setup(name)
    File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\conf\__init__.py", line 39, in _setup
        % (desc, ENVIRONMENT_VARIABLE))
    django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.

注意:我添加问题只是为了提供可能对某人有用的答案。

【问题讨论】:

【参考方案1】:

见https://github.com/AndreyMZ/jb_django_test_runner/blob/master/README.md。

优点:

    它适用于 PyCharm 2019.3.2。 输出窗口显示测试结果而不是错误“测试框架意外退出”。

【讨论】:

【参考方案2】:

1。背景资料

我只与 Django 合作了大约 3 个月 关于 PyCharm,我使用它已经有几年了,但只是作为一个 IDE(例如 PyCharm for dummies),所以我没有深入研究它的高级内容

考虑到上述情况,解决方案的某些(或全部)部分对于一些高级用户来说可能看起来很麻烦/愚蠢,所以请多多包涵。我会在解决方案中加入任何可能增加价值的评论。

回到问题:我对一个项目进行了测试/研究,该项目由 Django 教程 ([DjangoProject]: Writing your first Django app) + Django Rest Framework 教程 中的一些部分组成( [DRF]: Quickstart)。例如,我将尝试运行 polls/tests.pyQuestionViewTests.test_index_view_with_no_questions()

作为注释,DJANGO_SETTINGS_MODULE设置为异常指示,触发另一个,等等...

2。创建 Python 配置

虽然这不是问题的答案(它只是远程相关),但我还是发布了它(我相信很多人已经这样做了):

点击菜单运行 -> 编辑配置...运行/调试配置对话框中: 添加具有以下类型的新配置:Python工作目录设置为项目的根路径(对我来说是“E:\Work\Dev\Django\Tutorials\proj0\src”) .默认情况下,这也会在 Python 的模块搜索路径中添加路径 将 Script 设置为您的 Django 项目启动脚本 (manage.py) 将脚本参数设置为测试参数(test QuestionViewTests.test_index_view_with_no_questions) 为您的配置命名(可选)并单击确定。现在,您将能够运行此测试

当然,必须为每个测试用例(及其方法)都这样做不是可行的方法(确实很烦人),因此这种方法不可扩展。

3。调整 PyCharm 来做我们想做的事情

请注意,我不认为这是一个真正的解决方案,它更像是一种(蹩脚的)解决方法 (gainarie),而且它也具有侵入性。

让我们先来看看当我们在 测试RClick 时会发生什么(我将使用这个术语一般性 - 它可能意味着测试用例或方法或整体测试文件,除非另有说明)。对我来说,它正在运行以下命令:

"E:\Work\Dev\VEnvs\py2713x64-django\Scripts\python.exe" "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py" E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py::QuestionViewTests::test_index_view_with_no_questions true

如您所见,它正在启动“C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py”(我将其称为utrunner) 带有一堆参数(1st 对我们很重要,因为它是测试规范)。 utrunner 使用了一个不关心 Django 的测试运行框架(实际上有一些 Django 处理代码,但这对我们没有帮助)。

关于PyCharm运行/调试配置的几句话:

RClicktest 上运行时,PyCharm 会自动创建一个新的 Run 配置(您将能够保存),就像您在 Run/Debug Configurations 对话框中所做的一样。 重要需要注意的配置类型是 Python 测试/单元测试(会自动触发 utrunner) 一般来说,当创建运行配置时,PyCharm“复制”该配置类型默认值的设置(可以在Run/Debug Configurations 对话框),进入新配置,并用特定数据填充其他配置。 默认配置的一件重要的事情是它们是基于项目的:它们位于 .idea 文件夹(workspace.xml em>),因此修改它们不会影响其他项目(正如我最初担心的那样)

考虑到以上内容,让我们继续:

第一件事您需要做的是:从 Run/Debug Configurations 对话框(菜单:Run -> Edit Configurations... ),编辑 Defaults/Python tests/Unittests 设置:

设置工作目录就像之前的方法一样 在环境变量中添加一个名为DJANGO_TEST_MODE_GAINARIE的新变量并将其设置为任何字符串(除了空/null

第二件事和更棘手的一件事(也涉及入侵):修补utrunner

utrunner.patch

--- utrunner.py.orig    2016-12-28 19:06:22.000000000 +0200
+++ utrunner.py 2017-03-23 15:20:13.643084400 +0200
@@ -113,7 +113,74 @@
   except:
     pass

-if __name__ == "__main__":
+
+def fileToMod(filePath, basePath):
+  if os.path.exists(filePath) and filePath.startswith(basePath):
+    modList = filePath[len(basePath):].split(os.path.sep)
+    mods = ".".join([os.path.splitext(item)[0] for item in modList if item])
+    return mods
+  else:
+    return None
+
+
+def utrunnerArgToDjangoTest(arg, basePath):
+  if arg.strip() and not arg.startswith("--"):
+    testData = arg.split("::")
+    mods = fileToMod(testData[0], basePath)
+    if mods:
+      testData[0] = mods
+      return ".".join(testData)
+    else:
+      return None
+  else:
+    return None
+
+
+def flushBuffers():
+  sys.stdout.write(os.linesep)
+  sys.stdout.flush()
+  sys.stderr.write(os.linesep)
+  sys.stderr.flush()
+
+
+def runModAsMain(argv, codeGlobals):
+  with open(argv[0]) as f:
+    codeStr = f.read()
+  sys.argv = argv
+  code = compile(codeStr, os.path.basename(argv[0]), "exec")
+  codeGlobals.update(
+    "__name__": "__main__",
+    "__file__": argv[0]
+    )
+  exec(code, codeGlobals)
+
+
+def djangoMain():
+  djangoTests = list()
+  basePath = os.getcwd()
+  for arg in sys.argv[1: -1]:
+    djangoTest = utrunnerArgToDjangoTest(arg, basePath)
+    if djangoTest:
+      djangoTests.append(djangoTest)
+  if not djangoTests:
+    debug("/ [DJANGO MODE] Invalid arguments: " + sys.argv[1: -1])
+  startupTestArgs = [item for item in os.getenv("DJANGO_STARTUP_TEST_ARGS", "").split(" ") if item]
+  startupFullName = os.path.join(basePath, os.getenv("DJANGO_STARTUP_NAME", "manage.py"))
+  if not os.path.isfile(startupFullName):
+    debug("/ [DJANGO MODE] Invalid startup file: " + startupFullName)
+    return
+  djangoStartupArgs = [startupFullName, "test"]
+  djangoStartupArgs.extend(startupTestArgs)
+  djangoStartupArgs.extend(djangoTests)
+  additionalGlobalsStr = os.getenv("DJANGO_STARTUP_ADDITIONAL_GLOBALS", "")
+  import ast
+  additionalGlobals = ast.literal_eval(additionalGlobalsStr)
+  flushBuffers()
+  runModAsMain(djangoStartupArgs, additionalGlobals)
+  flushBuffers()
+
+
+def main():
   arg = sys.argv[-1]
   if arg == "true":
     import unittest
@@ -186,3 +253,10 @@

   debug("/ Loaded " + str(all.countTestCases()) + " tests")
   TeamcityTestRunner().run(all, **options)
+
+
+if __name__ == "__main__":
+  if os.getenv("DJANGO_TEST_MODE_GAINARIE"):
+    djangoMain()
+  else:
+    main()

以上是diff ([man7]: DIFF(1))(或补丁 - 名称可以连用 - 我更喜欢(并将使用)补丁):它显示了 utrunner.py.orig(原始文件 - 我在开始修改之前保存,您不需要这样做)和 utrunner 之间的差异。 py(包含更改的当前版本)。我使用的命令是diff --binary -uN utrunner.py.orig utrunner.py(显然,在 utrunner 的文件夹中)。作为个人评论,patch 是更改 3rd 方源代码的首选形式(以保持对更改的控制和分离)。

patch 中的代码做了什么(可能比普通的 Python 代码更难理解):

main 块下的所有内容(if __name__ == "__main__": 或当前行为)都已移至名为 main 的函数中(以保持独立并避免错误更改它) 修改了 main 块,因此如果定义了环境变量 DJANGO_TEST_MODE_GAINARIE(并且不为空),它将遵循新的实现(djangoMain 函数),否则它将正常运行。新的实现: fileToModfilePath 中减去 basePath 并将差值转换为 Python 包样式。例如:fileToMod("E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py", "E:\Work\Dev\Django\Tutorials\proj0\src"),将返回polls.tests utrunnerArgToDjangoTest:使用前面的函数,然后添加类名(QuestionViewTests)和(可选)方法名(test_index_view_with_no_questions),所以在最后它将测试规范从 utrunner 格式 (E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py::QuestionViewTests::test_index_view_with_no_questions) 转换为 manage.py 格式 (polls.tests.QuestionViewTests.test_index_view_with_no_questions) flushBuffers:写入一个 eoln 字符并刷新 stdoutstderr 缓冲区(这是必需的,因为我注意到有时PyCharmDjango 的输出是交错的,最终的结果是一团糟) runModAsMain:通常,所有相关的 manage.py 代码都在if __name__ == "__main__": 下。此函数“欺骗”Python 使其相信 manage.py 是作为其 1st 参数运行的

修补utrunner

我自己做了这些修改(我没有搜索具有 Django 集成的版本并从那里获得灵感) utrunnerPyCharm 的一部分。很明显,为什么 JetBrains 伙计们没有在 Community Edition 中包含 任何 Django 集成:让人们购买 Professional版。这有点踩到他们的脚趾。我不知道修改 utrunner 的法律影响,但无论如何,如果你修补它,你是在自己承担责任和风险 编码风格:很糟糕(至少从命名/缩进 PoV 来看),但它与文件的其余部分保持一致(应该允许编码风格糟糕的唯一情况)。 [Python]: PEP 8 -- Style Guide for Python Code 包含 Python 的编码风格指南 补丁应用于原始文件(utrunner.py),具有以下属性(对于v2019.2.3仍然有效(最后检查:20190930)): 尺寸:5865 sha256sum: db98d1043125ce2af9a9c49a1f933969678470bd863f791c2460fe090c2948a0 应用补丁utrunner 位于“$PYCHARM_INSTALL_DIR/helpers/pycharm” 通常,$PYCHARM_INSTALL_DIR 指向: 尼克斯/usr/lib/pycharm-community Win:“C:\Program Files (x86)\JetBrains\PyCharm 2016.3”(适应你的版本号) 保存 patch 内容(在名为 utrunner.patch 的文件中,假设它位于 /tmp 下) Nix - 事情很简单,只需(cdutrunner 的文件夹并)运行 patch -i /tmp/utrunner.patch。 [man7]: PATCH(1) 是默认安装的实用程序(Ubtupatch dpkg 的一部分)。请注意,由于 utrunner.pyroot 所有,因此您需要 sudo Win - 要遵循类似的步骤,但由于没有原生的 patch 实用程序,事情变得更加棘手。但是,有一些解决方法: 使用Cygwin。在 Nix (Lnx) 的情况下,patch 实用程序可用,但 默认情况下不会安装 . 补丁 pkg 必须从Cygwin 安装程序显式 安装。我试过了,效果很好 有替代品(我没有尝试过): [SourceForge.GnuWin32]: Patch for Windows 理论上,[RedBean]: svn patch(任何客户端)应该能够应用补丁,但我不确定该文件是否应该是工作副本的一部分>. 手动应用补丁(不太需要的选项:)) 与 Nix 的情况一样,修补文件(很可能)必须由其中一位 管理员 完成。另外,请注意文件路径,如果它们包含空格,请确保(dbl)引用它们 恢复补丁: 备份无害(除了可用磁盘空间的PoV,或者当它们开始堆积时,管理它们变得很痛苦)。在我们的情况下不需要它们。为了恢复更改,只需对修改后的文件运行命令:patch -Ri /tmp/utrunner.patch,它会将其切换回其原始内容(它还将创建一个 utrunner.py.orig 文件修改后的内容;它实际上会切换 .py.py.orig 文件)。 尽管如此在修改它们之前总是备份 3rd-party 文件(尤其是当它们被某些工具/安装程序跟踪时),以便在出现问题时在修改它们时,总有办法恢复原始状态 虽然这里不是这样,但如果更改是其他形式,例如应用了 patch 的文件(例如在GitHub),您显然可以获得整个文件(如果有有很多文件,跟踪所有文件可能会很痛苦)并覆盖你的。但同样,首先支持它(他们)

关于这种方法的几句话

代码可以处理(可选)环境变量(DJANGO_TEST_MODE_GAINARIE 除外 - 这是强制性的):

DJANGO_STARTUP_NAME:如果 manage.py 有其他名称(无论出于何种原因?),或者位于 工作目录 之外的另一个文件夹中。这里有一个重要:指定文件路径时,请使用平台特定的路径分隔符:slash (/) Nix, bkslash (\) 用于 Win DJANGO_STARTUP_TEST_ARGSmanage.py test 接受的附加参数(运行 manage.py test --help 以获取整个列表)。在这里,我必须坚持 -k / --keepdb 保留测试数据库( test_$REGULAR_DB_NAME 默认或在 TEST 字典下的 settings 中设置)。在运行单个测试时,创建 DB(并应用所有迁移)并销毁它可能会很耗时(而且也很烦人)。此标志确保 DB 在最后不会被删除,并将在下一次测试运行时重用 DJANGO_STARTUP_ADDITIONAL_GLOBALS:这必须具有 Python dict 的字符串表示形式。出于某种原因,manage.py 要求出现在 globals() 字典中的任何值都应放在此处

修改默认配置时,所有之前创建的继承它的配置不会更新,因此必须手动删除它们(并且将由新的RClick在他们的测试中自动重新创建)

RClick 在同一个测试中(删除之前的配置 :d),

E:\Work\Dev\VEnvs\py2713x64-django\Scripts\python.exe "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py" E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py::QuestionViewTests::test_index_view_with_no_questions true
Testing started at 01:38 ...


Using existing test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.390s

OK

Preserving test database for alias 'default'...


Process finished with exit code 0

调试也有效(断点等...)。

注意事项(目前我确定了其中 2 个):

这是良性的,它只是一个 UI 问题:utrunner(很可能)有一些 PyCharm 期望发生的初始化,这在我们的情况下显然不是。因此,即使测试成功结束,从 PyCharmPoV 中他们也没有,因此 Output 窗口将包含一个警告:“ 测试框架意外退出" 这是一个令人讨厌的问题,我(还)无法深入了解它。显然,在 utrunner 中,任何 input(raw_input) 调用都没有得到很好的处理;提示文本:“如果您想尝试删除测试数据库 'test_tut-proj0',请输入 'yes',或取消:'no':”(如果之前的测试运行崩溃,则会出现,并且它的 DB 最后没有被破坏)没有被显示并且程序冻结(这不会发生在 utrunner 之外),而不让用户输入文本(也许混合中有线程?)。恢复的唯一方法是停止测试运​​行,删除 DB 并再次运行测试。同样,我必须推广 manage.py test -k 标志,这将解决这个问题

我在以下环境上工作/测试过:

尼克斯Lnx): Ubtu 16.04 x64 PyCharm 社区版 2016.3.3 Python 3.4.4 (VEnv) Django 1.9.5 W10 x64 PyCharm 社区版 2016.3.2 Python 2.7.13 (VEnv) Django 1.10.6

注意事项

我将继续调查当前的问题(至少是第 2 个ndclean 解决方案是在 PyCharm 中以某种方式覆盖单元测试运行默认设置(我从代码中所做的),但我找不到任何配置文件(可能它在 PyCharm 罐子里?) 我注意到 helpersutrunner 的父文件夹)中有很多特定于 Django 的文件/文件夹,也许是那些也可以用,一定要检查

正如我在开头所说,任何建议都非常受欢迎!

@EDIT0

正如我在回复 @Udi 的评论时所说,对于那些无力(或不愿)支付 PyCharm 专业版 许可费的人(或公司不愿意)来说,这是一种替代方案(快速浏览它看起来是 ~100$-200$ 每个实例/年)

【讨论】:

或者:购买pycharm专业版,django有很多很棒的功能。 确实如此(或者至少我在屏幕截图中看到),但对于一些可能负担不起每年 100 美元-200 美元的人(或者一些公司干脆拒绝支付这些金额)来说,这是一个替代方案)。 第一个问题可以通过使用来自teamcity-messages的测试运行器teamcity.django.TeamcityDjangoRunner来修复。

以上是关于从 PyCharm 社区版中的鼠标右键单击上下文菜单运行/调试 Django 应用程序单元测试?的主要内容,如果未能解决你的问题,请参考以下文章

怎样设置鼠标右键菜单

MS Access 2010 运行时 - 连续表单中缺少鼠标右键单击上下文菜单

在 Ckeditor 5 中监听鼠标右键(上下文菜单)

无需在 Chrome 中单击鼠标右键即可检查元素

如何防止鼠标右键单击onMouseDown()反应js?

如何在 Mac 上右键单击?