2015-04-18 41 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

嗯有一個更簡單的方法,我只是意外發現。我會自我回答,儘管你的安全性更高。 –