在site-packages中安装我的Python单元测试是否有意义?

15

我正在开发我的第一个Python发行包。我的Python打包学习曲线似乎有所平稳,但我仍然在处理一些问题,其中一个问题是是否应该将单元测试与代码一起安装。

我了解到将测试用例包含在源分发中非常重要。我想知道的是,我是否应该实际上配置它们进行安装?

我看到至少一个流行的软件包故意这样做(PyHamcrest),还有至少一个不小心这样做(behave)。

所以我的问题有几个部分:

  • 是否有必要将我的软件包单元测试与软件包代码一起安装?

  • 如果是,那么使用场景是什么?谁会使用它们,为什么要使用?也就是说,谁会使用它们而不愿意只需下载源分发并运行python setup.py test

  • 他们如何使用已安装的单元测试?如import test; test.run()还是其他什么方式?


1
从我的角度来看,能够确定软件包是否正常工作/安装是非常有用的,特别是当您将其分发到不同平台并且具有可能因版本而异的第三方依赖项时。 - Joel Cornett
2
嗨,Joel,我在这里提出的问题是为什么安装测试而不仅仅将其包含在源分发中可能是有意义的。 - scanny
1
numpy scipy pandas sympy blaze numba skimage 都已经安装了测试文件在 site-packages 中,这也值得一提。 - endolith
@endolith 它们是否有明确的命名方式(例如像 'numpy_tests' 这样)?或者当您 import test 时得到的是基于安装顺序之类的随机命名? - scanny
@scanny 它们就像numpy.linalg.testsnumpy.fft.testsscipy.fftpack.testspanda.testssympy.integrals.testsskimage.segmentation.testsblaze.testsnumba.tests等测试。 - endolith
3个回答

14

我认为正确的答案是“否”,但你会发现很多发行版本都安装了测试。测试不应该被安装,而应该包含在源分发中。在我的看法中,在理想的世界中,安装软件包的测试应该是软件包管理器(pip)执行的任务,而site-packages目录不应该被测试源代码污染。

最近我研究了这个话题,并从各种来源收集了信息,找到了几种包含库源代码和测试的分发目录/包层次结构。大部分这样的结构似乎已经过时,并且它们是为了解决当时较旧的分发系统功能不完整的尝试而发明的。不幸的是,许多在线资源(较旧的博客文章/文档)仍然在宣传过时的方法,因此在在线搜索中很容易找到过时的分发教程。

假设你有一个名为“my_lib”的库,你想构建你的分发源代码的结构。我将展示两种流行且看起来过时的构建分发的方式,以及我发现的最通用的第三种方式。第三种方法也可能已经过时,但这是我发布这个答案时所知道的最好的方法。;-)

方法一

(有意或无意地)安装测试的发行版通常使用此方法。

层次结构

+- my_lib
|  +- __init__.py
|  +- source1.py
|  +- source2.py
|  +- tests
|     +- __init__.py
|     +- test_1.py
|     +- test_2.py
+- setup.py

第二种方法

测试未安装,但应通过 MANIFEST.in 文件包含在源分发中。

层次结构

+- my_lib
|  +- __init__.py
|  +- source1.py
|  +- source2.py
+- tests
|  +- __init__.py
|  +- test_1.py
|  +- test_2.py
+- setup.py

方法 #3(我更喜欢这种方法。)

这与方法 #2非常相似,只是有一点不同(src目录)。

层次结构

+- src
|  +- my_lib
|     +- __init__.py
|     +- source1.py
|     +- source2.py
+- tests
|  +- __init__.py
|  +- test_1.py
|  +- test_2.py
+- setup.py

setup() 在 setup.py 中的调用

from setuptools import setup, find_packages

setup(
    ...
    packages=find_packages('src'),
    package_dir={'': 'src'},
    ...
)

MANIFEST.in

recursive-include tests *.py

测试将不会被安装,但是它们将通过我们的MANIFEST.in包含在源分发中。

对于方法#3,您拥有一个src目录,通常只包含一个作为库根的单个包。将my_lib包放入src目录中(目录而不是包,因此您不需要src/__init__.py)具有以下优点:

  • 当您执行setup.py时,包含setup.py的目录会隐式添加到Python路径中。这意味着,在您的setup.py中,如果它的包与setup.py在同一个目录中,您可能会意外并错误地从您的库导入东西。通过将my_lib包放入src中,我们可以避免这个问题。
  • 您可以轻松地使用您分发的测试源来测试分发的库源和安装的库:

    • 当您使用setup.py test运行测试时,setup()调用中的package_dir = {'': 'src'}部分保证您的测试将看到您保存在src/my_lib中的my_lib库包。
    • 您也可以不使用setup.py来运行测试。在最简单的情况下,您可以使用python -m unittest命令来完成。在这种情况下,src目录不会成为Python路径的一部分,因此您可以使用此方法来测试安装的库版本而不是src中的源。

1
方法3布局避免的陷阱也在pytest良好的集成实践中描述:https://docs.pytest.org/en/latest/goodpractices.html#choosing-a-test-layout-import-rules 我自己也曾经犯过这些错误,所以开始遵循这个实践。 - SuperGeo

11
经过研究,除非有更有经验的人反对,我的理解是:单元测试不应该被安装,只应该包含在源代码分发中。 我发现几个安装了测试的案例,在这些情况下都是偶然发生的,而且很容易犯这个错误却没有注意到。 以下是出现这种情况的原因: 1. 在setup.py中使用了"packages=find_packages()"参数,以便可以找到包而不必显式列出它们。 2. "test"文件夹被转换成一个包(通过添加 "__init__.py"),以便测试可以使用相对命名(如"from ..import pkg.mod")引用它们测试的模块 3. setuptools会将"test"作为一个单独的包安装在项目的其他包旁边。请注意,这意味着您可以在python解释器中执行"import test",并且它会工作,几乎肯定不是您想要的,特别是因为许多其他人也使用这个名称来命名他们的测试目录:) 解决方法是使用设置:"packages=find_packages(exclude=['test'])"防止安装您的测试目录。

顺便问一下,你如何测试软件包的安装情况?使用源分发中的测试吗?我的意思是是否有特定的命令行或函数调用来执行此操作? - n611x007
你还记得是哪几个案例吗? - n611x007

2

虽然我不是专家,但我想分享我的意见。

如果我预计某些外部因素可能导致代码失败,我会将测试与代码放在一起。无论是位序、奇怪的时区、字符编码、24位整数还是其他任何你可能遇到并为其编写测试的奇异情况。

谁不愿意下载源代码并运行测试呢?也许有一些debian用户,他们从源代码中删除了软件包(我知道你在谈论Python,但让我稍微通俗一点),你的库可能会由于系统中的某些奇怪问题而偶尔失败。

如果你的测试只保证内部的正确性,那么我会跳过它们,因为没有源代码,它们就没有多大价值,因为你永远不会更改库的内部结构。

就我个人而言,我听说过一个东西失败了,因为它被移动到一台IBM机器上,该机器具有不同的位序。我不记得它是否依赖于位操作还是是否有一些预先计算和静态缓存。但有时候检查你加载的内容是否与你认为的相同是明智的。

编辑: 也许重新表述会更好。当你感觉可能存在可移植性注意事项时,我会安装测试。我认为在将东西部署到不同的系统上时,检查一下是很好的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,