我应该如何构建一个仅用于 pytest 测试的包?

thi*_*ybk 6 python testing pytest

我的用例是关于远程(RESTful API 等)测试整个系统的子系统。这意味着“pytest 仅测试包”对生产代码没有任何依赖(意味着其他生产代码 python 包)。

我创建了一个 python 包,其中仅包含与测试相关的内容,例如 pytest 测试、pytest 固定装置、util 模块中的测试辅助函数、 pytests conftest.py、 pytestspytest.ini等。它不包含任何与生产代码相关的内容。

现在功能可以正常工作,但包的结构相当“hacky”。这意味着安装无法正常工作(test、fixture 和 conftest 文件未正确安装到site_packagesvia中MANIFEST.ini),并且必须“手动”完成包的部署。

在 pytest 文档中,我刚刚找到了有关如何构建包含生产和 pytest 测试代码的包的最佳实践:应用程序代码外部的测试作为应用程序代码的一部分的测试测试目录结构

  1. 我应该如何构造一个只包含测试代码的Python包?是否有替代的封装结构(优点、缺点)?
  2. 测试文件及其依赖项(固定装置、帮助程序等)应该安装到哪里?

2.的可能解决方案:avocado-framework将示例测试部署为. 取决于默认部署的测试配置。setup.py/usr/share/avocado/tests

hoe*_*ing 7

要求

\n\n

在我看来,您需要满足三个要求:

\n\n
    \n
  1. 安装的测试应该很容易发现
  2. \n
  3. 出于开发目的,测试仍应可在本地运行
  4. \n
  5. 在开发模式和安装模式下运行测试应该产生相同的结果
  6. \n
\n\n

项目结构

\n\n

考虑到这一点,我将如何构建该项目:创建一些虚拟包(但具有唯一的可区分名称)并将测试目录以及conftest.py实用程序模块放入其中。它看起来是这样的:

\n\n
project/\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 setup.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 mypkg/\n \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n \xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 tests/\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conftest.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 utils.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 other_utils.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 test_spam.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 test_eggs.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 other_tests/  # if you need to further grouping of tests\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conftest.py  # if you need special conftest for other tests\n     \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test_bacon.py\n
Run Code Online (Sandbox Code Playgroud)\n\n

setup.py

\n\n
project/\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 setup.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 mypkg/\n \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n \xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 tests/\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conftest.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 utils.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 other_utils.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 test_spam.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 test_eggs.py\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 other_tests/  # if you need to further grouping of tests\n \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conftest.py  # if you need special conftest for other tests\n     \xc2\xa0\xc2\xa0  \xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test_bacon.py\n
Run Code Online (Sandbox Code Playgroud)\n\n

有了这个项目结构:

\n\n
    \n
  1. 测试在本地目录中正确发现并执行project/

    \n\n
    $ pytest -v\n============================= test session starts =============================\nplatform darwin -- Python 3.6.3, pytest-3.3.2, py-1.5.2, pluggy-0.6.0 -- /Users\n/hoefling/.virtualenvs/stackoverflow/bin/python\ncachedir: .cache\nrootdir: /Users/hoefling/projects/private/stackoverflow/so-48111426, inifile:\nplugins: forked-0.2, asyncio-0.8.0, xdist-1.22.0, mock-1.6.3, hypothesis-3.44.4\ncollected 3 items\n\nspam/tests/test_eggs.py::test_foo PASSED                                 [ 33%]\nspam/tests/test_spam.py::test_bar PASSED                                 [ 66%]\nspam/tests/other_tests/test_bacon.py::test_baz PASSED                    [100%]\n\n========================== 3 passed in 0.03 seconds ===========================\n
    Run Code Online (Sandbox Code Playgroud)
  2. \n
  3. 当您构建源 tar 或 Wheel 并安装软件包时,通过提供软件包名称可以轻松运行测试:

    \n\n
    $ pytest -pyargs mypkg\n...\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    您将获得完全相同的测试结果,因为pytest发现测试的方式与在本地运行测试时相同,只是不扫描当前工作目录,而是扫描包的目录。

  4. \n
  5. 尽管所有测试以及 config 和 utils 都安装在 中site-packages,但它们本身并不是包或模块。对于外界来说,发行版只包含一个空包mypkg;中的任何内容tests/都是不重要的并且仅对 可见pytest

  6. \n
\n\n

为什么data_files不可靠

\n\n

当然,您可以在设置脚本中声明类似的内容:

\n\n
from setuptools import setup\n\nsetup(\n    name=\'mypkg-tests\',\n    version=\'0.1\',\n    install_requires=[\'pytest\'],\n    packages=[\'mypkg\'],\n    package_data={\'mypkg\': [\'tests/*\', \'tests/**/*\']},\n)\n
Run Code Online (Sandbox Code Playgroud)\n\n

首先,创建和维护要包含的测试列表并不那么方便(尽管肯定可以使用os.walkpathlib.glob或其他方式编写某种自定义测试查找)。但更重要的是,您将无法可靠地将其安装data_files到绝对路径。我想在这里详细讨论;请随意查看我的其他答案以获取更多信息 - 但基本上,它是一个wheel将每个绝对路径相对化的包data_filessys.prefix即使您构建了一个源代码发行版,pip install也会首先用它构建一个轮子,然后安装车轮。因此,如果您想从已安装的软件包运行测试,您首先需要sys.prefix自己确定并构建路径:

\n\n
$ SYS_PREFIX=$(python -c "import sys; print(sys.prefix)")\n$ pytest -v $SYS_PREFIX/whatever/dir/mapped/in/data_files/tests\n
Run Code Online (Sandbox Code Playgroud)\n\n

避免此wheel问题的唯一方法是构建源代码发行版并使用pip install mypkg --no-binary=mypkg. 此选项将强制pip跳过轮构建步骤并直接从源安装。只有这样测试才会安装到绝对路径。我发现这很不方便,因为有时您会忘记参数no-binary并花时间寻找错误源。或者有人必须在您不在场指导他时安装该软件包,并且无法运行测试。根本不使用data_files.

\n