Biopython测试框架
Biopython有一个回归测试框架(文件 run_tests.py
) based on unittest ,Python的标准单元测试框架。为模块提供全面的测试是确保Biopython代码在推出之前尽可能无错误的最重要方面之一。它也往往是贡献中最被低估的方面之一。本章旨在使运行Biopython测试和编写良好的测试代码尽可能简单。理想情况下,进入Biopython的每个模块都应该有测试(并且还应该有文档!)。强烈鼓励我们所有的开发人员以及从源代码安装Biopython的任何人运行单元测试。
运行测试
当您下载Biopython源代码或从我们的源代码存储库中检查它时,您应该会发现一个Inbox调用 Tests
.这包含关键脚本 run_tests.py
,许多单独的剧本命名 test_XXX.py
,以及许多其他子目录,其中包含测试套件的输入文件。
作为构建和安装Biopython的一部分,您通常会使用以下内容从Biopython源顶级目录在命令行上运行完整的测试套件:
$ python setup.py test
这实际上相当于去 Tests
调试并运行:
$ python run_tests.py
您通常只想运行一些测试,这是这样完成的:
$ python run_tests.py test_SeqIO.py test_AlignIO.py
在给出测试列表时, .py
扩展是可选的,所以您也可以只需输入:
$ python run_tests.py test_SeqIO test_AlignIO
要运行文档字符串测试(请参阅部分 编写文档测试 ),你可以使用
$ python run_tests.py doctest
您还可以通过添加添加显式在线组件来跳过任何使用显式在线组件设置的测试 --offline
,例如:
$ python run_tests.py --offline
默认情况下, run_tests.py
运行所有测试,包括文档字符串测试。
如果单个测试失败,您还可以尝试直接运行它,这可能会为您提供更多信息。
基于Python标准的测试 unittest
框架将 import unittest
然后定义 unittest.TestCase
类,每个类都有一个或多个子测试作为方法,从 test_
它检查代码的某些特定方面。
使用Tox运行测试
与大多数Python项目一样,您也可以使用 Tox 对多个Python版本运行测试,前提是它们已安装在您的系统中。
我们不提供配置 tox.ini
由于难以确定用户特定的设置(例如Python版本的可执行文件名称),因此将文件放入我们的代码库中。您可能只对仅针对我们支持的Python版本的子集测试Biopython感兴趣。
如果您有兴趣使用Tox,可以从示例开始 tox.ini
如下所示:
[tox]
envlist = pypy,py38,py39
[testenv]
changedir = Tests
commands = {envpython} run_tests.py --offline
deps =
numpy
reportlab
使用上面的模板,执行 tox
将针对PyPy、Python 3.8和3.9测试您的Biopython代码。它假设这些Python的可执行文件被命名为Python 3.8的“python3.8”,以此类推。
编写测试
假设您想为名为 Biospam
.这可以是您编写的模块,也可以是尚未进行任何测试的现有模块。在下面的例子中,我们假设 Biospam
是一个进行简单数学运算的模块。
每个Biopython测试都由一个包含测试本身的脚本组成,还可以选择包含测试使用的输入文件的目录:
test_Biospam.py
- 您模块的实际测试代码。Biospam
[optional]– A directory where any necessary input files will be located. If you have any output files that should be manually reviewed, output them here (but this is discouraged) to prevent clogging up the main Tests directory. In general, use a temporary file/folder.
任何带有 test_
中的前置 Tests
将找到并运行目录 run_tests.py
.下面,我们展示了一个示例测试脚本 test_Biospam.py
.如果你把这个剧本放在Biopython中 Tests
目录,然后 run_tests.py
将找到它并执行其中包含的测试:
$ python run_tests.py
test_Ace ... ok
test_AlignIO ... ok
test_BioSQL ... ok
test_BioSQL_SeqIO ... ok
test_Biospam ... ok
test_CAPS ... ok
test_Clustalw ... ok
...
----------------------------------------------------------------------
Ran 107 tests in 86.127 seconds
使用编写测试 unittest
的 unittest
-framework has been included with Python since version 2.1, and is documented in the Python Library Reference (which I know you are keeping under your pillow, as recommended). There is also online documentation for unittest .如果你熟悉 unittest
系统(或类似鼻子测试框架的东西),您应该不会遇到任何麻烦。您可能会发现查看Biopython中的现有示例也很有帮助。
这是一个最小的 unittest
- 风格测试脚本 Biospam
,您可以复制和粘贴即可开始:
import unittest
from Bio import Biospam
class BiospamTestAddition(unittest.TestCase):
def test_addition1(self):
result = Biospam.addition(2, 3)
self.assertEqual(result, 5)
def test_addition2(self):
result = Biospam.addition(9, -1)
self.assertEqual(result, 8)
class BiospamTestDivision(unittest.TestCase):
def test_division1(self):
result = Biospam.division(3.0, 2.0)
self.assertAlmostEqual(result, 1.5)
def test_division2(self):
result = Biospam.division(10.0, -2.0)
self.assertAlmostEqual(result, -5.0)
if __name__ == "__main__":
runner = unittest.TextTestRunner(verbosity=2)
unittest.main(testRunner=runner)
在分区测试中,我们使用 assertAlmostEqual
instead of assertEqual
to avoid tests failing due to roundoff errors; see the unittest
chapter in the Python documentation for details and for other functionality available in unittest
(online reference ).
这些是 unittest
- 基于测试:
测试用例存储在从
unittest.TestCase
并涵盖代码的一个基本方面你可以使用方法
setUp
和tearDown
对于每个测试方法之前和之后应该运行的任何重复代码。比如说setUp
方法可能用于创建您正在测试的对象的实例,或打开文件柄。的tearDown
应该进行任何“整理”,例如关闭文件手柄。测试的开头有
test_
每个测试都应该覆盖你要测试的内容的一个特定部分。你可以在一个类中有尽可能多的测试。在测试脚本的最后,您可以使用
if __name__ == "__main__": runner = unittest.TextTestRunner(verbosity=2) unittest.main(testRunner=runner)
当脚本单独运行(而不是从
run_tests.py
).如果您运行此脚本,那么您会看到类似以下内容:$ python test_BiospamMyModule.py test_addition1 (__main__.TestAddition) ... ok test_addition2 (__main__.TestAddition) ... ok test_division1 (__main__.TestDivision) ... ok test_division2 (__main__.TestDivision) ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.059s OK
为了更清楚地指示每个测试正在做什么,您可以向每个测试添加文档字符串。这些在运行测试时显示,如果测试失败,这些信息可能是有用的信息。
import unittest from Bio import Biospam class BiospamTestAddition(unittest.TestCase): def test_addition1(self): """An addition test""" result = Biospam.addition(2, 3) self.assertEqual(result, 5) def test_addition2(self): """A second addition test""" result = Biospam.addition(9, -1) self.assertEqual(result, 8) class BiospamTestDivision(unittest.TestCase): def test_division1(self): """Now let's check division""" result = Biospam.division(3.0, 2.0) self.assertAlmostEqual(result, 1.5) def test_division2(self): """A second division test""" result = Biospam.division(10.0, -2.0) self.assertAlmostEqual(result, -5.0) if __name__ == "__main__": runner = unittest.TextTestRunner(verbosity=2) unittest.main(testRunner=runner)
运行脚本现在将向您显示:
$ python test_BiospamMyModule.py An addition test ... ok A second addition test ... ok Now let's check division ... ok A second division test ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.001s OK
如果您的模块包含文档字符串测试(请参阅部分 编写文档测试 ),你 may 希望将这些内容包括在要运行的测试中。您可以通过修改下面的代码来执行以下操作 if __name__ == "__main__":
看起来像这样:
if __name__ == "__main__":
unittest_suite = unittest.TestLoader().loadTestsFromName("test_Biospam")
doctest_suite = doctest.DocTestSuite(Biospam)
suite = unittest.TestSuite((unittest_suite, doctest_suite))
runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
runner.run(suite)
只有当您想在执行时运行文档字符串测试时,这才有意义 python test_Biospam.py
如果它有一些复杂的运行时依赖性检查。
一般来说,通过将文档字符串测试添加到 run_tests.py
如下所述。
编写文档测试
Python模块、类和函数支持使用文档字符串的内置文档。的 doctest framework (包含在Python中)允许开发人员在文档字符串中嵌入工作示例,并自动测试这些示例。
目前Biopython中只有一部分包含文档测试。的 run_tests.py
脚本负责运行doctests。为此,在 run_tests.py
脚本是手动编译的要跳过的模块列表,对于可能未安装的可选外部依赖项(例如Reportlab和NumPy库)来说很重要。因此,如果您已将一些文档测试添加到Biopython模块中的文档字符串中,则为了将它们排除在Biopython测试套件中,您必须更新 run_tests.py
包含您的模块。目前,相关部分 run_tests.py
看起来如下:
# Following modules have historic failures. If you fix one of these
# please remove here!
EXCLUDE_DOCTEST_MODULES = [
"Bio.PDB",
"Bio.PDB.AbstractPropertyMap",
"Bio.Phylo.Applications._Fasttree",
"Bio.Phylo._io",
"Bio.Phylo.TreeConstruction",
"Bio.Phylo._utils",
]
# Exclude modules with online activity
# They are not excluded by default, use --offline to exclude them
ONLINE_DOCTEST_MODULES = ["Bio.Entrez", "Bio.ExPASy", "Bio.TogoWS"]
# Silently ignore any doctests for modules requiring numpy!
if numpy is None:
EXCLUDE_DOCTEST_MODULES.extend(
[
"Bio.Affy.CelFile",
"Bio.Cluster",
# ...
]
)
请注意,我们主要将doctests视为文档,因此您应该坚持典型使用。通常处理错误条件等的复杂示例最好留给专门的单元测试。
请注意,如果您想编写涉及文件解析的文档测试,则定义文件位置会使事情变得复杂。理想情况下使用相对路径,假设代码将从 Tests
目录,请参阅 Bio.SeqIO
文档测试了这方面的一个例子。
要仅运行文档字符串测试,请使用
$ python run_tests.py doctest
请注意,docTest系统很脆弱,需要小心确保您的输出将与Biopython支持的所有不同版本的Python匹配(例如浮点数的差异)。
在收件箱中编写文档测试
您正在阅读的这个收件箱包含很多代码片段,这些代码片段通常格式为docTest。我们有自己的系统档案 test_Tutorial.py
允许将收件箱源中的标记代码片段作为Python文档测试运行。这是通过添加特殊来实现的 .. doctest
每个Python控制台(pycon)块之前的注释行,例如
.. doctest
.. code:: pycon
>>> from Bio.Seq import Seq
>>> s = Seq("ACGT")
>>> len(s)
4
通常代码示例不是独立的,而是从之前的Python块继续。这里我们使用神奇的评论 .. cont-doctest
如此处所示:
.. cont-doctest
.. code:: pycon
>>> s == "ACGT"
True
特别 .. doctest
评论行可以采用工作目录(相对于 Doc/
文件夹)如果您有任何示例数据文件(例如 .. doctest examples
将使用 Doc/examples
文件夹,而 .. doctest ../Tests/GenBank
将使用 Tests/GenBank
文件夹.
在目录参数之后,您可以通过添加 lib:XXX
以指示 import XXX
必须工作,例如 .. doctest examples lib:numpy
您可以通过以下方式运行Docttests:
$ python test_Tutorial.py
或者:
$ python run_tests.py test_Tutorial.py