在nose或pytest中收集以编程方式生成的测试套件的好方法

quo*_*tor 9 python nose pytest

说我有一个像这样的测试套件:

class SafeTests(unittest.TestCase):
    # snip 20 test functions

class BombTests(unittest.TestCase):
    # snip 10 different test cases
Run Code Online (Sandbox Code Playgroud)

我目前正在做以下事情:

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)
Run Code Online (Sandbox Code Playgroud)

我有一个重大问题,一个有趣的观点

  • 我想使用nose或py.test(doest并不重要)
  • 我有大量不同的应用程序通过入口点暴露这些测试套件.

    我希望能够在所有已安装的应用程序中聚合这些自定义测试,因此我不能使用一个聪明的命名约定.我并不 特别关心通过入口点暴露这些内容,但我 确实关心能够在站点包中的应用程序之间运行测试.(不仅仅导入......每个模块.)

关心维持当前的依赖性 unittest.TestCase,摧毁依赖性实际上是一个目标.


编辑这是为了确认@ Oleksiy关于传递args的观点 nose.run实际上与一些警告有关.

事情工作:

  • 传递所有想要执行的文件(这很奇怪)
  • 传递所有想要执行的模块.(这可能不执行任何操作,错误操作或执行太多操作.可能是0,1或者许多有趣的情况?)
  • 在目录之前传递模块:目录必须先到,否则你将得到重复的测试.

这种脆弱是荒谬的,如果你有改进它的想法我欢迎评论,或者我设置了 一个github回购与我的实验试图让这个工作.

除此之外,以下工作,包括获取安装到site-packages中的多个项目:

#!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()
Run Code Online (Sandbox Code Playgroud)

如果保存到runtests.py文件中,则在运行时执行以下操作:

runtests.py project.tests otherproject.tests
Run Code Online (Sandbox Code Playgroud)

quo*_*tor 1

事实证明这是一团糟:Nose 几乎专门使用该 TestLoader.load_tests_from_names函数(这是在 中测试的 唯一函数unit_tests/test_loader),所以因为我想实际从任意 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)
Run Code Online (Sandbox Code Playgroud)