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

2025-02-28 08:22:00
admin
原创
72
摘要:问题描述:我必须强调PyCharm社区版没有任何Django集成(提问时为v 2016.3.2)。我已经在 Google 上搜索了我的问题,但(令人惊讶的是)我没有得到任何答案,(当然我不排除可能有一些答案,但我只是错过了)。问题很简单:在PyCharm中,只需右键单击(从上下文菜单中)即可运行(调试)单元测...

问题描述:

我必须强调PyCharm社区没有任何Django集成(提问时为v 2016.3.2)。

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

问题很简单:在PyCharm中,只需右键单击(从上下文菜单中)即可运行(调试)单元测试(TestCase或其方法之一),如下图所示:

在 RClick 上运行 Django 单元测试

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

Traceback (most recent call last):
    File "C:InstallPyCharm Community Edition6.3.2helperspycharm/utrunner.py", line 254, in <module>
        main()
    File "C:InstallPyCharm Community Edition6.3.2helperspycharm/utrunner.py", line 232, in main
        module = loadSource(a[0])
    File "C:InstallPyCharm Community Edition6.3.2helperspycharm/utrunner.py", line 65, in loadSource
        module = imp.load_source(moduleName, fileName)
    File "E:WorkDevDjangoTutorialsproj0srcpolls    ests.py", line 7, in <module>
        from polls.models import Question
    File "E:WorkDevDjangoTutorialsproj0srcpollsmodels.py", line 9, in <module>
        class Question(models.Model):
    File "E:WorkDevDjangoTutorialsproj0srcpollsmodels.py", line 10, in Question
        question_text = models.CharField(max_length=200)
    File "E:WorkDevVEnvspy2713x64-djangolibsite-packagesdjangodbmodelsields__init__.py", line 1043, in __init__
        super(CharField, self).__init__(*args, **kwargs)
    File "E:WorkDevVEnvspy2713x64-djangolibsite-packagesdjangodbmodelsields__init__.py", line 166, in __init__
        self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
    File "E:WorkDevVEnvspy2713x64-djangolibsite-packagesdjangoconf__init__.py", line 53, in __getattr__
        self._setup(name)
    File "E:WorkDevVEnvspy2713x64-djangolibsite-packagesdjangoconf__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:

  1. 背景信息


  • 我只使用Django大约 3 个月

  • 至于PyCharm,我使用过几年,但只是把它当作一个 IDE(就像PyCharm for dummies一样),所以我没有深入研究它的高级功能

考虑到上述情况,解决方案的某些部分(或全部)可能对某些高级用户来说显得繁琐/愚蠢,所以请耐心等待。我会将任何可能增加价值的评论纳入解决方案中。

回到问题:我在一个由Django 教程([DjangoProject]: 编写你的第一个 Django 应用程序)+Django Rest Framework 教程([DRF]: 快速入门)的一些部分组成的项目上进行了测试/研究。作为示例,我将尝试运行polls/tests.pyQuestionViewTests.test_index_view_with_no_questions()

需要注意的是,按照异常指示设置DJANGO_SETTINGS_MODULE会触发另一个异常,依此类推...

  1. 创建Python配置


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

  • 点击菜单运行->编辑配置...

  • 运行/调试配置对话框中:

  • 添加具有以下类型的新配置:Python

  • 将工作目录设置为项目的根路径(对我来说是“ E:\Work\Dev\Django\Tutorials\proj0\src ”)。默认情况下,这还会将该路径添加到Python的模块搜索路径中

  • 脚本设置为你的Django项目启动脚本 ( manage.py )

  • 将脚本参数设置为测试参数(test QuestionViewTests.test_index_view_with_no_questions

  • 为您的配置命名(可选),然后单击“确定”。现在,您将能够运行此测试

当然,对每个测试用例(及其方法)都这样做并不是可行的方法(这确实很烦人),所以这种方法不可扩展。

  1. 调整PyCharm来做我们想要的事情


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

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

"E:WorkDevVEnvspy2713x64-djangoScriptspython.exe" "C:InstallPyCharm Community Edition6.3.2helperspycharm/utrunner.py" E:WorkDevDjangoTutorialsproj0srcpolls    ests.py::QuestionViewTests::test_index_view_with_no_questions true

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

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

  • 单击测试时,PyCharm会自动创建一个新的运行配置(您可以保存),就像在运行/调试配置对话框中一样。需要注意的是配置类型是Python 测试/单元测试(会自动触发utrunner

  • 通常,在创建运行配置时, PyCharm会将该配置类型默认值中的设置(可在运行/调试配置对话框中查看)“复制”到新配置中,并用特定数据填充其他配置。关于默认配置的一个重要事项是它们是基于项目的:它们位于项目的.idea文件夹(workspace.xml)中,因此修改它们不会影响其他项目(正如我最初担心的那样)

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

您需要做的第一件事是:从运行/调试配置对话框(菜单:运行->编辑配置...)中,编辑默认/Python 测试/单元测试**设置:

  • 像以前的方法一样设置工作目录

  • 环境变量中添加一个名为DJANGO_TEST_MODE_GAINARIE的新变量,并将其设置为任何字符串(除空/ null之外)

第二件事也是比较棘手的事情(也涉及入侵):修补utrunner

utrunner.补丁

--- 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) )(或patch - 名称可以连用 - 我更喜欢(并将使用)patch ):它显示了utrunner.py.orig(原始文件 - 我在开始修改之前保存的,您不需要这样做)和utrunner.py(包含更改的当前版本)之间的差异。我使用的命令是diff --binary -uN utrunner.py.orig utrunner.py(显然,在utrunner的文件夹中)。作为个人评论,patch是更改第三方源代码的首选形式(以保持更改在控制之下并保持独立)。

补丁中的代码的作用(可能比纯Python代码更难理解):

  • 块(或当前行为)下的所有内容if __name__ == "__main__":都已移至名为main的函数中(以保持其独立并避免错误地更改它)

  • 修改了主块,因此如果定义了环境变量DJANGO_TEST_MODE_GAINARIE(且不为空),它将遵循新的实现(djangoMain函数),否则它将正常运行。新的实现:

    • fileToModfilePath中减去basePath,并将差值转换为Python包样式。例如:,将返回fileToMod("E:WorkDevDjangoTutorialsproj0srcpolls ests.py", "E:WorkDevDjangoTutorialsproj0src")`polls.tests`

    • utrunnerArgToDjangoTest:使用前一个函数,然后添加类名(QuestionViewTests)和(可选)方法名(test_index_view_with_no_questions),最后将测试规范从utrunner格式(E:WorkDevDjangoTutorialsproj0srcpolls ests.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是作为其第一个参数运行的

修补utrunner

  • 我自己做了这些修改(我没有搜索具有Django集成的版本并从那里获得灵感)

  • utrunner是PyCharm的一部分。JetBrains人没有在社区版中包含任何Django集成的原因很明显:让人们购买专业版。这有点冒犯了他们。我不知道修改utrunner的法律含义,但无论如何,如果你修补它,你要自己承担责任和风险**

  • 编码风格:很糟糕(至少从命名/缩进PoV来看),但与文件的其余部分一致(唯一允许编码风格糟糕的情况)。[Python]:PEP 8 -- Python 代码风格指南包含Python的编码风格指南

  • 补丁程序应用于原始文件(utrunner.py),具有以下属性(对于v 2019.2.3(上次检查:20190930)仍然有效):

    • 尺寸:5865

    • sha256sum:db98d1043125ce2af9a9c49a1f933969678470bd863f791c2460fe090c2948a0

  • 应用补丁

    • utrunner位于“ ${PYCHARM_INSTALL_DIR}/helpers/pycharm

    • 通常,${PYCHARM_INSTALL_DIR}指向:

      • Nix/usr/lib/pycharm-community

      • Win:“ C:\Program Files (x86)\JetBrains\PyCharm 2016.3 ”(适应您的版本号)

    • 保存补丁内容(在名为utrunner.patch的文件中,假设它位于/tmp下)

    • Nix - 事情很简单,只需(cdutrunner的文件夹并)运行patch -i /tmp/utrunner.patch。[man7]:PATCH(1)是默认安装的实用程序(Ubtu中patch dpkg的一部分)。请注意,由于utrunner.py由root拥有,因此对于此步骤,您需要sudo

    • Win - 遵循类似的步骤,但由于没有原生补丁实用程序,因此事情比较棘手。不过,有解决方法:

      • 使用Cygwin。与Nix ( Lnx ) 的情况一样,补丁实用程序可用,但默认情况下不会安装。必须从Cygwin 设置中明确安装补丁pkg 。我试过了,它有效

      • 还有其他方法(我没有尝试过):

        • 理论上,[RedBean]: svn patch(任何客户端)都应该能够应用补丁,但我不确定该文件是否应该是工作副本的一部分。

        • 手动应用补丁(不太理想的选择:))

      • Nix的情况一样,修补文件(最有可能)必须由管理员之一完成。此外,请注意文件路径,如果文件路径包含空格,请确保使用双引号括起来

    • 恢复补丁

      • 备份不会造成任何损害(除非从可用磁盘空间的角度来看,或者当它们开始堆积起来时,管理它们会变得很麻烦)。在我们的例子中,不需要它们。为了恢复更改,只需在修改后的文件上运行命令:patch -Ri /tmp/utrunner.patch,它会将其切换回其原始内容(它还将创建一个包含修改后内容的utrunner.py.orig文件;它实际上会切换.py.py.orig文件)。
        尽管如此,在修改第三方文件之前,请务必备份它们(特别是如果它们被某些工具/安装程序跟踪),这样如果在修改它们时出现问题,总有办法恢复原始状态

    • 虽然这里不是这种情况,但如果更改是以另一种形式进行的,例如应用了补丁的文件(例如在GitHub上),您显然可以获取整个文件(如果文件很多,追踪所有文件可能会很麻烦)并覆盖您的文件。但同样,请先备份它(它们)

关于这种方法的几点说明

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

+ *DJANGO_STARTUP_NAME*:如果*manage.py*有其他名称(无论出于何种原因?),或者位于*工作目录*以外的其他文件夹中。这里**需要注意的**是:指定文件路径时,请使用特定于平台的路径分隔符:*Nix为**斜杠*( ***/*** ) , *Win*为*反斜杠*( ***\\ )***
+ *DJANGO_STARTUP_TEST_ARGS*:接受的附加参数`manage.py test`(运行`manage.py test --help`以获取整个列表)。在这里,我必须坚持使用***-k*** / ***--keepdb***,它在运行之间保留测试数据库(默认情况下为*test_${REGULAR_DB_NAME}或在**TEST*字典下的*设置*中设置)。运行单个测试时,创建*数据库*(并应用所有迁移)并销毁它可能非常耗时(而且非常烦人)。此标志可确保数据库*在*最后不会被删除,并将在下一次测试运行中重新使用
+ *DJANGO_STARTUP_ADDITIONAL_GLOBALS :必须具有**Python 字典*的字符串表示形式。出于某种原因, *manage.py*要求字典中存在的任何值`globals()`都应放在此处
  • 修改默认配置时,所有继承该默认配置的先前创建的配置都不会更新,因此必须手动删除它们(并且将由新的RClick测试中自动重新创建)

单击相同的测试(删除其先前的配置后:d),然后

E:WorkDevVEnvspy2713x64-djangoScriptspython.exe "C:InstallPyCharm Community Edition6.3.2helperspycharm/utrunner.py" E:WorkDevDjangoTutorialsproj0srcpolls    ests.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期望进行的初始化,而在我们的例子中显然没有。因此,即使测试成功结束,从PyCharm角度来看,它们并没有成功结束,因此输出窗口将包含一个警告:“测试框架意外退出

  • 这是一个棘手的问题,我暂时无法弄清楚。显然,在utrunner中,任何input( raw_input) 调用都处理得不太好;提示文本:“如果您想尝试删除测试数据库 'test_tut-proj0',请输入 'yes',或输入 'no' 取消: ”(如果上一次测试运行崩溃,并且其数据库在最后没有被销毁,则会出现此提示)没有显示,程序冻结(这不会发生在utrunner之外),不让用户输入文本(可能是线程混杂?)。恢复的唯一方法是停止测试运​​行,删除数据库并再次运行测试。再次,我必须提升manage.py test -k可以解决这个问题的标志

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

  • 尼克斯Lnx):

    • Ubtu 16.04 x64

    • PyCharm 社区版 2016.3.3

    • Python 3.4.4VEnv

    • Django 1.9.5

    • W10 x64

    • PyCharm 社区版 2016.3.2

    • Python 2.7.13VEnv

    • Django 1.10.6

注意

  • 我将继续调查当前的问题(至少是第二个问题)

  • 一个干净的解决方案是在PyCharm中以某种方式覆盖单元测试运行的默认设置(我从代码中所做的),但我找不到任何配置文件(可能是在PyCharm jars 中?)

  • 我注意到helpersutrunner的父级)文件夹中有很多特定于Django 的文件/文件夹,也许这些也可以使用,必须检查一下

正如我在一开始所说的,我们非常欢迎任何建议!

@EDIT0

  • 正如我回复@Udi 的评论一样,对于那些负担不起(或公司不愿意)支付PyCharm 专业版许可费用的人来说,这是一种替代方案(快速浏览一下,每个实例每年的费用约为100-200 美元)

解决方案 2:

请参阅https://github.com/AndreyMZ/jb_django_test_runner/blob/master/README.md

优点:

  1. 它与 PyCharm 2019.3.2 兼容。

  2. 输出窗口显示测试结果而不是错误“测试框架意外退出”。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2941  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1803  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色。然而,在实际运行中,资源冲突是经常会遇到的难题。资源冲突可能导致项目进度延迟、成本增加以及产品质量下降等一系列问题,严重影响企业的效益与竞争力。因此,如何有效应对PLM系统中的资源冲突,成为众多企业关注的焦点。接下来,我们将详细探讨5...
plm项目管理系统   31  
  敏捷项目管理与产品生命周期管理(PLM)的融合,正成为企业在复杂多变的市场环境中提升研发效率、增强竞争力的关键举措。随着技术的飞速发展和市场需求的快速更迭,传统的研发流程面临着诸多挑战,而将敏捷项目管理理念融入PLM,有望在2025年实现研发流程的深度优化,为企业创造更大的价值。理解敏捷项目管理与PLM的核心概念敏捷项...
plm项目   31  
  模块化设计在现代产品开发中扮演着至关重要的角色,它能够提升产品开发效率、降低成本、增强产品的可维护性与可扩展性。而产品生命周期管理(PLM)系统作为整合产品全生命周期信息的关键平台,对模块化设计有着强大的支持能力。随着技术的不断发展,到 2025 年,PLM 系统在支持模块化设计方面将有一系列令人瞩目的技术实践。数字化...
plm软件   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用