小编典典

从PyCharm Community Edition中的鼠标右键单击上下文菜单运行/调试Django应用程序的UnitTests?

python

我必须强调的 PyCharm 社区 版_这也 没有任何 Django的 集成( _v 2016年3月2日 在质询时间)。

Google 已经解决了我的问题,并且(令人惊讶的是)我没有得到任何答案,(当然,我没有排除可能有答案的可能性,但是我只是错过了它们)。

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

在RClick上运行Django单元测试

不幸的是,这产生了一个例外:

Traceback (most recent call last):
    File "C:\Install\PyCharm Community

Edition\2016.3.2\helpers\pycharm\utrunner.py”, line 254, in
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
from polls.models import Question
File “E:\Work\Dev\Django\Tutorials\proj0\src\polls\models.py”, line
9, in
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.

注意 :我仅添加了问题,以提供可能对某人有用的答案。


阅读 331

收藏
2021-01-20

共1个答案

小编典典

1.背景资料

  • 我只在 Django上 工作了大约3个月
  • 关于 PyCharm ,我已经使用了几年,但仅作为一个IDE(例如 傻瓜的PyCharm ),所以我没有深入研究它的高级知识

考虑到上述情况,对于某些高级用户而言,解决方案的某些(或全部)部分可能看起来很繁琐/愚蠢,因此请耐心等待。我将结合任何可能增加解决方案价值的评论。

回到问题:我对一个包含 Django Tutorial
[DjangoProject]:编写您的第一个Django应用)和
Django Rest Framework Tutorial[DRF]:Quickstart)的某些部分的项目进行了测试/研究。例如,我将尝试运行 polls /
tests.py
QuestionViewTests.test_index_view_with_no_questions()

请注意, DJANGO_SETTINGS_MODULE 设置 为异常指示, 触发另一个触发 ,依此类推…

2.创建 Python 配置

尽管这不是问题的答案(仅与远程相关),但无论如何我还是在发布它(我相信已经有很多人这样做了):

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

当然,不必为每个测试用例(及其方法)执行此操作(这确实很烦人),因此该方法不可扩展。

3.调整 PyCharm 以执行我们想要的操作

请注意,我不认为这是一个真正的解决方案,它更像是(lame)解决方法( 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:\安装\ PyCharm社区版\ 2016年3月2日\佣工\ pycharm \ utrunner.py
”(我要去把它称为 utrunner )与一堆参数(1日对我们来说很重要,因为这是测试规范。 utrunner 使用的测试运行框架并不关心
Django (实际上有一些 Django 处理代码,但这对我们没有帮助)。

关于 PyCharmRun / Debug配置的 几句话:

  • RClick 进入 测试时PyCharm会 自动创建一个新的 “运行”配置 (您将能够保存),就像在 “运行/调试配置” 对话框中一样。需要注意的 重要 一点是配置类型,即 Python测试/单元测试 (它会自动触发 utrunner
  • 通常,在创建 运行配置 时, PyCharm 从该配置类型“ 默认值” (可以在“ 运行/调试配置” 对话框中查看)“复制”设置到新配置中,并用特定数据填充其他配置。关于 默认配置的 重要一件事是它们 基于项目 :它们位于 项目.idea 文件夹( workspace.xml )中,因此修改它们不会影响其他项目(正如我首先担心的那样)

考虑到以上几点,让我们继续:

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

  • 像以前的方法一样设置 工作目录
  • 环境变量中, 添加一个名为 DJANGO_TEST_MODE_GAINARIE 的新 变量, 并将其设置为任何字符串(empty / 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))(或一个 补丁 -名称可以结合使用-我推荐(并将使用) patch ):它显示了
utrunner.py.orig (原始文件-我在开始修改之前保存的文件,您不需要这样做)和 utrunner.py
(包含更改的当前版本)。我使用的命令是diff --binary -uN utrunner.py.orig utrunner.py(显然在
utrunner 的文件夹中)。作为个人的话, 补丁 是改变3优选形式RD方的源代码(控制住的变化,并分离)。

补丁 中的代码做什么(可能比纯 Python 代码更难遵循):

  • main 块下的所有内容(if __name__ == "__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 format(polls.tests.QuestionViewTests.test_index_view_with_no_questions)。
    • flushBuffers :编写一个 eoln char并刷新 stdoutstderr 缓冲区(这是必需的,因为我注意到有时 PyCharmDjango 的输出是交错的,最终结果被弄乱了)
    • runModAsMain :通常,所有相关的 manage.py 代码都在下if __name__ == "__main__":。该函数“欺骗” Python ,使其认为 manage.py 作为其第一个参数运行

修补 utrunner

  • 我自己进行了这些修改(我没有搜索具有 Django 集成的版本并从中得到启发)
  • utrunnerPyCharm的 一部分。很明显,为什么 JetBrains的 人没有 Community Edition中 包括 任何 Django 集成:让人们购买 Professional Edition 。 这有点踩他们的脚趾。我不知道修改 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} 指向:
    • 尼克斯/ usr / lib / pycharm-community
    • Win :“ C:\ Program Files(x86)\ JetBrains \ PyCharm 2016.3 ”(适应您的版本号)
    • 保存 补丁 内容(在一个名为 utrunner.patch 的文件中,让我们假设它位于 / tmp下
    • 尼克斯 -事情很容易,只需运行(将其 CDutrunner 的文件夹并运行)即可patch -i /tmp/utrunner.patch[man7]:PATCH(1)是默认安装的实用程序( Ubtu中补丁 dpkg的 一部分 )。请注意,由于 utrunner.pyroot 拥有,对于此步骤,您将需要 sudo
    • -遵循类似的步骤,但由于没有本机 修补 程序实用程序,因此操作更加棘手。但是,有解决方法:
    • 使用Cygwin。与 NixLnx )情况一样, 补丁 程序实用程序可用,但 默认情况下未安装 。该 补丁 PKG必须 明确地 从安装 Cygwin的设置 。我尝试了这个并且 有效
    • 还有其他选择(我没有尝试过):
    • Nix 的情况一样,修补文件(很可能)必须由一位 Administrators完成 。另外,请注意文件路径,如果文件路径包含空格,请确保用 (dbl)引号
    • 还原 补丁
    • 备份无害(除了可用磁盘空间的 PoV之外 ,或者当它们开始堆积时,管理它们变得很麻烦)。在我们的情况下,不需要它们。为了恢复更改,只需在修改后的文件:上运行命令patch -Ri /tmp/utrunner.patch,它将切换回其原始内容(它还将创建具有修改后内容的 utrunner.py.orig 文件;实际上它将切换 。 py.py.orig 文件)。
      不过 总是回到3 次三方修改之前文件了(特别是如果他们被一些工具/安装跟踪),这样,如果在修改他们不顺心的事,总有恢复原来状态的方法

    • 虽然这里不是这种情况,但是如果更改是以另一种形式进行的,例如应用了 补丁 的文件(例如,在GitHub上),则显然可以获取整个文件(如果有很多文件,则将其全部向下跟踪可能会成为痛苦)并覆盖您的。但是同样, 请先将其备份

关于这种方法的几句话

  • 该代码可以处理(可选)env var( DJANGO_TEST_MODE_GAINARIE除外 -这是强制性的):

    • DJANGO_STARTUP_NAME :如果 manage.py 具有其他名称(出于某种原因?),或者位于 Working目录 之外的其他文件夹中。一个 重要的 这里的事情:在指定文件路径时,使用特定于平台的路径分隔符: 斜线 / 对) 尼克斯bkslash __ )为
    • DJANGO_STARTUP_TEST_ARGSmanage.py test接受的其他参数(运行manage.py test --help以获取整个列表)。在这里,我必须坚持使用 -k / --keepdb 来在 两次运行之间 保留测试数据库(默认情况下 为test _ $ {REGULAR_DB_NAME} 或在 TEST 字典下的设置中进行 设置 )。在运行单个测试时,创建 数据库 (并应用所有迁移)并销毁它可能很耗时(并且也很烦人)。该标志确保 数据库 不会在最后删除,并在下一次测试运行时被重用 __
    • DJANGO_STARTUP_ADDITIONAL_GLOBALS :必须具有 Python字典 的字符串表示形式。出于某种原因, manage.py 要求globals()字典中存在的任何值都应放在此处
    • 修改 默认配置时 ,所有继承它的先前创建的配置 都不会被更新 ,因此必须手动将其 删除 (新 RClick会 在其 测试中 自动重新创建它们)

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

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

调试也可以(断点等)。

注意事项 (到目前为止,我确定了其中两个):

  • 这是良性的,它只是一个 UI 问题: utrunner (最有可能)具有 PyCharm 希望进行的某些初始化,在我们的情况下显然不存在。因此,即使测试成功结束,它们也不会从 PyCharmPoV中 退出 ,因此“ 输出” 窗口将包含警告:“ 测试框架意外退出
  • 这是一个令人讨厌的内容,但我还无法深入了解它。显然,在 utrunner中 ,对任何inputraw_input)调用的处理都不太好;提示文本:“ 如果要尝试删除测试数据库’test_tut-proj0’,请输入’yes’,否则请单击’no’以取消: ”(如果先前的测试运行崩溃,并且未破坏其 数据库 ,则会出现结束)不会被显示,并且程序会冻结(这不会在 utrunner 之外 发生 ),而不会让用户输入文本(可能是线程中有线程?)。恢复的唯一方法是停止测试运​​行,删除 数据库, 然后再次运行测试。再次,manage.py test -k

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

  • 尼克斯Lnx ):
    • 乌布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 jar中?)
  • 我注意到在 helpersutrunner 的父文件夹)文件夹中有很多特定于 Django 的文件/文件夹,也许那些也可以使用,因此必须检查

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

@ EDIT0

  • 正如我回复@Udi的评论一样,对于无法负担(或不愿意支付) PyCharm Professional Edition 许可费的人(在快速浏览中,它的价格 约为100 $ -200) $ /年(每个实例)
2021-01-20