2015-04-18 43 views
11

我有一个包含目录“tests”的包,用于存储我的单元测试。我的包的样子:递归单元测试发现

. 
├── LICENSE 
├── models 
│   └── __init__.py 
├── README.md 
├── requirements.txt 
├── tc.py 
├── tests 
│   ├── db 
│   │   └── test_employee.py 
│   └── test_tc.py 
└── todo.txt 

从我的包目录,我希望能够找到既tests/test_tc.pytests/db/test_employee.py。我不希望有安装第三方库(nose或等),或者必须手工创建一个TestSuite在运行此。

一旦它找到了一个

肯定有办法告诉unittest discover没有停止寻找测试? python -m unittest discover -s tests会发现tests/test_tc.pypython -m unittest discover -s tests/db会发现tests/db/test_employee.py。没有办法找到两者吗?

+0

你看过http://stackoverflow.com/q/644821/3001761?我认为其中的一些答案可能会适合您的目的。 – jonrsharpe

回答

28

在做一些挖掘工作时,似乎只要深层模块可以导入,它们将通过python -m unittest discover发现。然后,解决方案只需将一个__init__.py文件添加到每个目录以使它们成为包。

. 
├── LICENSE 
├── models 
│   └── __init__.py 
├── README.md 
├── requirements.txt 
├── tc.py 
├── tests 
│   ├── db 
│   │   ├── __init__.py  # NEW 
│   │   └── test_employee.py 
│   ├── __init__.py   # NEW 
│   └── test_tc.py 
└── todo.txt 

只要每个目录中有一个__init__.pypython -m unittest discover可以导入相关test_*模块。

+3

我认为这是正确的答案。 –

4

如果您可以在测试中添加__init__.py文件,那么您可以在其中添加一个load_tests函数来处理发现。

如果测试包的名称(与__init__.py目录)的 图案然后包将要“load_tests”函数来检查匹配。如果 存在,那么它将被加载器,测试,模式调用。

如果load_tests存在,则发现确实不是递归到包中, load_tests负责加载包中的所有测试。

我远离相信这是最好的方式,但编写函数的一种方法是:

import os 
import pkgutil 
import inspect 
import unittest 

# Add *all* subdirectories to this module's path 
__path__ = [x[0] for x in os.walk(os.path.dirname(__file__))] 

def load_tests(loader, suite, pattern): 
    for imp, modname, _ in pkgutil.walk_packages(__path__): 
     mod = imp.find_module(modname).load_module(modname) 
     for memname, memobj in inspect.getmembers(mod): 
      if inspect.isclass(memobj): 
       if issubclass(memobj, unittest.TestCase): 
        print("Found TestCase: {}".format(memobj)) 
        for test in loader.loadTestsFromTestCase(memobj): 
         print(" Found Test: {}".format(test)) 
         suite.addTest(test) 

    print("=" * 70) 
    return suite 

很丑陋,我同意。

首先将所有子目录添加到测试包的路径(Docs)。

然后,您使用pkgutil来寻找软件包或模块。

当它找到一个时,它会检查模块成员以查看它们是否是类,如果它们是类,那么它们是否是unittest.TestCase的子类。如果是,则类中的测试会加载到测试套件中。

所以,现在,从您的项目里面的根,你可以键入

python -m unittest discover -p tests 

使用-p模式开关。如果一切顺利的话,你会看到我所看到的,这是一样的东西:

Found TestCase: <class 'test_tc.TestCase'> 
    Found Test: testBar (test_tc.TestCase) 
    Found Test: testFoo (test_tc.TestCase) 
Found TestCase: <class 'test_employee.TestCase'> 
    Found Test: testBar (test_employee.TestCase) 
    Found Test: testFoo (test_employee.TestCase) 
====================================================================== 
.... 
---------------------------------------------------------------------- 
Ran 4 tests in 0.001s 

OK 

这是什么预期,我的每一个两个示例文件包含了两个测试,testFootestBar每个。

编辑:一些更多的挖掘之后,它看起来像,你可以指定这个功能:

def load_tests(loader, suite, pattern): 
    for imp, modname, _ in pkgutil.walk_packages(__path__): 
     mod = imp.find_module(modname).load_module(modname) 
     for test in loader.loadTestsFromModule(mod): 
      print("Found Tests: {}".format(test._tests)) 
      suite.addTests(test) 

它使用loader.loadTestsFromModule()方法,而不是我上面使用的loader.loadTestsFromTestCase()方法。它仍然修改tests软件包路径,并让它寻找模块,我认为这是模块的关键。

输出现在看起来有点不同,因为我们每次添加一个测试套件发现我们的主要测试套件suite

python -m unittest discover -p tests 
Found Tests: [<test_tc.TestCase testMethod=testBar>, <test_tc.TestCase testMethod=testFoo>] 
Found Tests: [<test_employee.TestCase testMethod=testBar>, <test_employee.TestCase testMethod=testFoo>] 
====================================================================== 
.... 
---------------------------------------------------------------------- 
Ran 4 tests in 0.000s 

OK 

但是,我们仍然获得4次测试,我们预计,在这两个班,在两个子目录中。

+1

这比'python -m unittest discover -s tests --recursive'难得多,我真的很希望在投票结束之前有人会发表评论。 (也就是说,*谢谢!*) –

+1

@AdamSmith没有问题,我希望它有帮助 - 经过一番挖掘,看起来有一种更简单的方法,更依赖单元测试发现,而不是我有明确的检查 - 我会编辑我的答案以包含此替代方案。 – jedwards

+0

嗯有一个更简单的方法,我只是意外发现。我会自我回答,尽管你的安全性更高。 –