【问题标题】:unittest, works locally, but not on a remote server, no module named x.__main__; 'x' is a package and cannot be directly executedunittest,在本地工作,但不在远程服务器上,没有名为 x.__main__ 的模块; 'x' 是一个包,不能直接执行
【发布时间】:2020-07-03 01:37:41
【问题描述】:

我正在为我的 Python 包开发 Jenkins CI/CD 管道。我的项目文件层次结构如下:

project/
- package_name
  - file1.py
  - file2.py
  - etc...
- tests
  - unit
    - __main__.py
    - __init__.py
    - test1.py
    - test2.py

所有单元测试(我正在使用unittest)都使用单个命令运行

python -m tests.unit

通过添加__init__.py以下内容:

内容

import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))

__main__.py 看起来像这样

内容

import unittest
import sys

sys.path.append('../..')

loader = unittest.TestLoader()
start_dir = '.'
suite = loader.discover(start_dir)
runner = unittest.TextTestRunner(verbosity=2).run(suite)

首先,将路径更改为./tests/unit。之后,将顶层目录添加到导入路径中,以便可以在测试中导入包。这可以在我的个人笔记本电脑 (Python 3.6.4) 上按预期工作(即,通过在项目目录顶部运行 python -m test.unit 来执行所有休息)。

但是,当我在远程 Jenkins 服务器(Python 3.6.4 也是)上使用相同的技巧时,我收到以下错误:

no module named test.unit.__main__; 'test.unit' is a package and cannot be directly executed

我已经研究过这个问题,但建议的解决方案似乎都不适用于我的情况。

如何修改我的代码以在 unittest 中创建一个测试套件,该套件将在本地和远程运行而不会出现任何问题?

编辑 我尝试修改PYTHONPATH 变量,但没有成功

【问题讨论】:

  • 如果你使用 unittest,你可以运行python -m unittest discover。您不需要__main__.py。请参阅docs。您也可以删除__init__.py 的内容。
  • @mark_s 它确实有 init 文件,这个包是一个合法的 Python 包
  • 我还不熟悉 Jenkins,问这个我觉得有点傻,但是远程服务器在运行你的代码时并没有默认使用 Python 2.7 是吗? Google 对您的错误消息的一些响应将其与 Python 2.x 版相关联。
  • @mark_s 两个系统中相同版本的 Python(3.6.4,本地和远程)
  • 您确定问题不是由python -m test.unitpython -m tests.unit 错字引起的(注意单个字母的区别)?至少您的问题文本表明可能是这种情况。如果您的问题中有错字,请编辑它,我将删除我的评论。

标签: python unit-testing jenkins module python-unittest


【解决方案1】:

1。为什么它不起作用?

1.1。关于python -m__main__.py

当你运行python -m tests.unit时,python解释器会运行什么代码,是这样的

tests.__init__.py
tests.unit.__init__.py
tests.unit.__main__.py

1.2。重现错误

现在,如果您删除__main__.py,您将收到以下错误:

No module named tests.unit.__main__; 'tests.unit' is a package and cannot be directly executed

这与您收到的消息几乎相同。如果您的sys.path 中有一个文件夹,其中包含名为test 的文件夹,其结构如下(注意:test 文件夹不在prular 中,并且没有__main__.py!)

test
├── __init__.py
└── unit
    └── __init__.py

并运行命令

python -m test.unit

python 解释器试图运行的东西,按这个顺序是

test.__init__.py
test.unit.__init__.py
test.unit.__main__.py <-- missing!

由于缺少test.unit.__main__.py,您将收到错误消息

No module named test.unit.__main__; 'test.unit' is a package and cannot be directly executed

这是您收到的错误消息。因此,您的错误消息的原因很可能是您在 sys.path 目录中的某个位置有一个名为 test 的目录,其结构如上所示,并且您尝试调用 python -m test.unit 而不是 python -m tests.unit

2。如何让它发挥作用?

  • 删除您在__init__.py__main__.py 中使用的os.chdirsys.path.append hack。至少,python unittest 工作不需要它们。
  • 使用documentation 中显示的模式创建单元测试(通过继承unittest.TestCase)
  • 通过
  • 运行您的单元测试
python -m unittest

【讨论】:

    【解决方案2】:

    我可以使用以下命令运行测试:

    python -m unittest discover
    

    您永远不需要运行 os.chdir 或附加到 sys.path 来运行您的测试。

    我复制了您的项目结构,如下所示:

    .
    ├── package
    │   └── module.py
    └── tests
        ├── __init__.py
        └── unit
            ├── __init__.py
            └── test_module.py
    

    注意tests/ 下的每个目录都有一个__init__.py 文件,该文件实际上是空的。它只需要存在。它的存在使该目录成为一个模块。如需更多用例,请阅读此answer

    您不需要__main__.py。那是为了当你想从命令行运行python3 -m package(而不是python3 package/module.py或类似的东西)。看到这个answer

    在这种情况下,unittest 只需要测试目录中的__init__.py 文件即可正常工作。

    每个测试文件的名称以 test_ 开头也很重要,因为这是 unittest 寻找的命名约定。

    作为参考,module.py 的内容如下所示:

    def method():
        return True
    

    test_module.py 的内容是这样的:

    from package.module import method
    from unittest import TestCase
    
    class TestMethod(TestCase):
        def test_method(self):
            self.assertTrue(method())
    

    对您的代码进行这些修改将允许您在 unittest 中运行您的测试套件,而不会在本地和远程出现任何问题。

    【讨论】:

    • 感谢您的意见。我更有兴趣了解为什么将测试作为模块导入在一个地方有效,而在另一个地方无效。
    猜你喜欢
    • 2020-04-08
    • 2021-06-12
    • 2017-11-16
    • 1970-01-01
    • 2020-06-26
    • 1970-01-01
    • 2022-10-18
    • 2016-09-08
    • 2021-08-22
    相关资源
    最近更新 更多