Python 在nose或pytest中收集编程生成的测试套件的好方法
假设我有一个这样的测试套件:Python 在nose或pytest中收集编程生成的测试套件的好方法,python,nose,pytest,Python,Nose,Pytest,假设我有一个这样的测试套件: class SafeTests(unittest.TestCase): # snip 20 test functions class BombTests(unittest.TestCase): # snip 10 different test cases 我目前正在做以下工作: suite = unittest.TestSuite() loader = unittest.TestLoader() safetests = loader.loadTe
class SafeTests(unittest.TestCase):
# snip 20 test functions
class BombTests(unittest.TestCase):
# snip 10 different test cases
我目前正在做以下工作:
suite = unittest.TestSuite()
loader = unittest.TestLoader()
safetests = loader.loadTestsFromTestCase(SafeTests)
suite.addTests(safetests)
if TARGET != 'prod':
unsafetests = loader.loadTestsFromTestCase(BombTests)
suite.addTests(unsafetests)
unittest.TextTestRunner().run(suite)
我有一个大问题,还有一个有趣的问题
- 我想使用nose或py.test(哪种都不重要)
- 我有大量不同的应用程序公开这些测试 通过入口的套房 我希望能够在所有已安装的系统中聚合这些自定义测试 所以我不能仅仅使用一个聪明的命名约定。我不 特别关心这些通过入口点暴露,但我 是否关心是否能够在中跨应用程序运行测试 网站包。(不只是导入…每个模块。)
unittest.TestCase
,破坏这种依赖性实际上是一个目标
编辑这是为了确认@Oleksiy关于将参数传递给
nose.run
确实有一些警告
不起作用的事情:
- 传递想要执行的所有文件(这很奇怪)
- 传递要执行的所有模块。(这两种方法都可以执行 没有,错误的事情,或者太多的事情。有趣的例子是0,1或 很多,也许吧?)
- 在目录之前传入模块:目录必须到达 首先,否则您将得到重复的测试
#!python
import importlib, os, sys
import nose
def runtests():
modnames = []
dirs = set()
for modname in sys.argv[1:]:
modnames.append(modname)
mod = importlib.import_module(modname)
fname = mod.__file__
dirs.add(os.path.dirname(fname))
modnames = list(dirs) + modnames
nose.run(argv=modnames)
if __name__ == '__main__':
runtests()
如果将其保存到runtests.py
文件中,则在以以下方式运行时会执行正确的操作:
runtests.py project.tests otherproject.tests
如果您的问题是“如何让pytest‘查看’测试?”,则需要在每个测试文件和每个测试用例(即函数)前面加上“test”。然后,只需在pytest命令行上传递要搜索的目录,它将递归搜索与“test_XXX.py”匹配的文件,从这些文件中收集“test_XXX”函数并运行它们
至于文档,您可以尝试启动
如果您不喜欢默认的pytest测试收集方法,您可以使用nose的方向对其进行自定义。您可以将两个测试都设置到位,并使用插件选择要运行的测试,这对于选择要运行的测试非常有用。我将保留这两个测试并为它们分配属性:
from nose.plugins.attrib import attr
@attr("safe")
class SafeTests(unittest.TestCase):
# snip 20 test functions
class BombTests(unittest.TestCase):
# snip 10 different test cases
对于生产代码,我只需使用nosetests-a safe
调用nose,或者在操作系统生产测试环境中设置nose\u ATTR=safe
,或者在nose对象上调用run方法,以基于目标的-a
命令行选项在python中本机运行它:
import sys
import nose
if __name__ == '__main__':
module_name = sys.modules[__name__].__file__
argv = [sys.argv[0], module_name]
if TARGET == 'prod':
argv.append('-a slow')
result = nose.run(argv=argv)
最后,如果由于某种原因您的测试没有被发现,您可以使用@istest
属性(来自nose.tools import istest
)将它们显式标记为测试。结果是一团糟:nose几乎完全使用
TestLoader.load\u tests\u from\u names
函数(这是在中测试的唯一函数
)
因此,由于我想从任意python对象实际加载内容,我
似乎需要我自己写出来,弄清楚使用哪种加载函数
然后,另外,要正确地让事情像nosetests
脚本那样工作
我需要进口大量的东西。我一点也不确定这个
是做事情的最好方式,甚至不是那种。但这是一个精简版
对我有用的示例(无错误检查,不太详细):
import sys
import types
import unittest
from nose.config import Config, all_config_files
from nose.core import run
from nose.loader import TestLoader
from nose.suite import ContextSuite
from nose.plugins.manager import PluginManager
from myapp import find_test_objects
def load_tests(config, obj):
"""Load tests from an object
Requires an already configured nose.config.Config object.
Returns a nose.suite.ContextSuite so that nose can actually give
formatted output.
"""
loader = TestLoader()
kinds = [
(unittest.TestCase, loader.loadTestsFromTestCase),
(types.ModuleType, loader.loadTestsFromModule),
(object, loader.loadTestsFromTestClass),
]
tests = None
for kind, load in kinds.items():
if isinstance(obj, kind) or issubclass(obj, kind):
log.debug("found tests for %s as %s", obj, kind)
tests = load(obj)
break
suite = ContextSuite(tests=tests, context=obj, config=config)
def main():
"Actually configure the nose config object and run the tests"
config = Config(files=all_config_files(), plugins=PluginManager())
config.configure(argv=sys.argv)
tests = []
for group in find_test_objects():
tests.append(load_tests(config, group))
run(suite=tests)
如果您愿意更改代码以生成py.test
“套件”(我的定义)而不是unittest套件(技术术语),您可以很容易地做到这一点。创建一个名为conftest.py
的文件,如下所示
import pytest
def pytest_collect_file(parent, path):
if path.basename == "foo":
return MyFile(path, parent)
class MyFile(pytest.File):
def collect(self):
myname="foo"
yield MyItem(myname, self)
yield MyItem(myname, self)
class MyItem(pytest.Item):
SUCCEEDED=False
def __init__(self, name, parent):
super(MyItem, self).__init__(name, parent)
def runtest(self):
if not MyItem.SUCCEEDED:
MyItem.SUCCEEDED = True
print "good job, buddy"
return
else:
print "you sucker, buddy"
raise Exception()
def repr_failure(self, excinfo):
return ""
您将在其中生成代码/将代码添加到MyFile
和MyItem
类中(与unittest.TestSuite
和unittest.TestCase
相反)。我以这种方式保留了MyFile
类的命名约定,因为它旨在表示您从文件中读取的内容,但当然您基本上可以将其解耦(正如我在这里所做的)。有关这方面的官方示例,请参见。唯一的限制是,我编写的foo
必须作为文件存在,但您也可以将其解耦,例如使用conftest.py
或树中存在的任何其他文件名(并且只运行一次,否则所有匹配的文件都将运行—如果您不对树中存在的每个文件执行if path.basename
测试!!!)
您可以使用从命令行运行此命令
py.test -whatever -options
或者从你使用的任何代码中编程
import pytest
pytest.main("-whatever -options")
py.test的好处在于,您可以解锁许多非常强大的插件,例如不,我知道如何使用py.test,并且我的测试有正确的名称,我希望能够告诉pytest“这个由脚本生成的测试集合”。(我正在动态生成测试套件,因此命名基于标准名称的测试发现不起作用。)@quodlibetor:所以当你说你在“动态生成测试套件”时,你并不是在将它们写入文件(a la'a.example.com/test/test_XXX.py')但是它们是作为Python对象生成的吗?谢谢,@attr
标志非常好,解决了我问题中精心设计的部分,但我不知道如何将我的所有attr公开给外部应用程序?你不能从外部应用程序设置环境变量吗?我猜你说的是显式地传递所有mod运行nose.run
?除非nose知道如何自动加载测试套件,否则我认为这是行不通的。fwiw我试过了,并做了nose.run($VIRTALENV\u SITE\u PACKAGES/package/tests.py)
(请原谅伪pybash),除非从$VIRTUAL中运行它,否则它是行不通的