6.7 使用虚拟环境和tox

在第5章中,已经介绍并讨论了虚拟环境的使用。它的主要用途之一便是为单元测试提供干净的环境。当你认为你的测试工作正常但是实际上不正常时是相当郁闷的,如涉及依赖列表的情况。

可以写一个脚本去部署虚拟环境,安装setuptools,然后安装应用程序/库的运行时或者单元测试所需要的所有依赖。但这是非常常见的用例,所以已经有专门针对这一需求的应用程序了,即tox

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

tox的目标是自动化和标准化Python中运行测试的方式。基于这一目标,它提供了在一个干净的虚拟环境中运行整个测试集的所有功能,并安装被测试的应用程序以检查其安装是否正常。

使用tox之前,需要提供一个配置文件。这个文件名为tox.ini,需要放在被测试项目的根目录,与setup.py同级。

$ touch tox.ini

现在可以成功运行tox:

% tox
GLOB sdist-make: /home/jd/project/setup.py
python create: /home/jd/project/.tox/python
python inst: /home/jd/project/.tox/dist/project-1.zip
____________________ summary _____________________
  python: commands succeeded
  congratulations :)

显然这本身并不是很有用。在上面的例子中,tox使用默认的Python版本在.tox/python中创建了一个虚拟环境,使用setup.py创建了应用程序的一个分发包并在这个虚拟环境中进行了安装。接下来并没有命令运行,因为该配置文件中并没有指定任何命令。

可以通过添加一个要在测试环境中运行的命令来改变其默认行为。编辑tox.ini。让它包含以下内容:

[testenv]
commands=nosetests

要执行的nosetests命令很可能会失败,因为在该虚拟环境中我们并没有安装nosetests。因此需要将其作为(将被安装的)依赖的一部分列出来。

[testenv]
deps=nose
commands=nosetests

再次运行,tox会重建虚拟环境,安装新的依赖并运行nosetests命令,它将执行所有单元测试。显然,我们可能需要添加更多的依赖,这可以通过配置项deps列出,也可以使用-rfile语法从文件中读取。如果正在使用pbr管理setup.py文件,那么应该知道它是从一个名为requirements.txt的文件中读取所有依赖的。因此,让tox使用这个文件是一个好主意:

[testenv]
deps=nose
     -rrequirements.txt
commands=nosetests

文件中[testenv]一节定义的是被tox管理的所有虚拟环境参数。但正如前面所提及的,tox能够真正地管理多个Python虚拟环境,通过向tox传入-e标志就可以将测试运行在某个特定Python版本之上而不是运行在默认的版本之上。

% tox -e py26
GLOB sdist-make: /home/jd/project/setup.py
py26 create: /home/jd/project/.tox/py26
py26 installdeps: nose
py26 inst: /home/jd/project/.tox/dist/rebuildd-1.zip
py26 runtests: commands[0] | nosetests
.......

---------------------------------------------------------
Ran 7 tests in 0.029s

OK
____________________ summary _____________________
   py26: commands succeeded
   congratulations :)

默认情况下,tox可以模拟多种环境:py24py25py26py27py30py31py32py33jythonpypy!你甚至可以加入自定义的环境。要添加一个环境或者创建一个新环境,只需添加一个新的配置节[testenv:_envname_]。如果要针对其中的某个环境运行不同的命令,使用下面的tox.ini文件是很容易实现的:

[testenv]
deps=nose
commands=nosetests
[testenv:py27]
commands=pytest

这只覆盖了针对py27环境的命令,所以当运行tox -e py27时nose仍然会被作为依赖的一部分安装,但会执行pytest命令。

也可以使用Python不支持的版本创建新环境:

[testenv]
deps=nose
commands=nosetests

[testenv:py21]
basepython=python2.1

这里试图使用Python 2.1运行测试集,尽管我并不认为它能运行得起来。

如今,通常你可能希望应用程序能支持多个Python版本。让tox为想要默认支持的Python版本运行所有测试是非常有用的。这可以通过指定要使用的环境列表来实现,而在tox运行时无须提供参数。

[tox]
envlist=py26,py27,py33,pypy

[testenv]
deps=nose
commands=nosetests

当不指定任何参数运行tox时,列出的所有4种环境都将被创建,继而安装依赖和应用程序,然后运行命令nosetests

也可以使用tox来集成其他测试,如flake8,正如1.4节中讨论过的。

[tox]
envlist=py26,py27,py33,pypy,pep8

[testenv]
deps=nose
commands=nosetests

[testenv:pep8]
deps=flake8
commands=flake8

在这个示例中,使用默认的Python版本运行pep8环境,不过这应该问题不大1

 提示

  当运行tox时,你会发现所有的环境会按顺序创建并运行。这通常会令整个过程耗时很长。但因为虚拟环境都是隔离的,所以可以并行运行tox命令。这正是detox包(https://pypi.python.org/pypi/detox)要做的,即通过detox命令能够并行运行envlist中指定的所有默认环境。你应该运行pip install安装它。

1如果想修改它,还是可以指定basepython