2017-08-18 55 views
3

大多數測試框架假定「1測試= 1 Python方法/函數」, 並且考慮在函數執行時傳遞的測試,而不是 提升聲明。每個輸入文件分開測試用例?

我測試編譯樣的程序(一個程序,讀取*.foo 文件並處理其內容),爲此,我想很多輸入(*.foo)文件執行相同的測試。 IOW,我的測試是這樣的:

class Test(unittest.TestCase): 
    def one_file(self, filename): 
     # do the actual test 

    def list_testcases(self): 
     # essentially os.listdir('tests/') and filter *.foo files. 

    def test_all(self): 
     for f in self.list_testcases(): 
      one_file(f) 

我當前的代碼使用 unittest從 Python的標準庫,即one_file使用self.assert...(...) 語句來檢查測試是否通過。

這工作,因爲我得到一個計劃,成功感/失敗 時,我的代碼是OK /越野車,但我失去了很多的 優勢的測試框架:

  • 我沒有得到相關報告,如「X失敗的Y測試」,也沒有 通過/失敗的測試列表。 (我打算用這樣的系統 不僅考驗我自己的發展也年級學生的代碼 作爲一個老師,所以報告是非常重要的對我來說)

  • 我沒有得到測試的獨立性。第二個測試運行在第一個等待的環境 上,依此類推。第一次失敗停止 測試套件:失敗後的測試用例根本沒有運行。

  • 我得到我濫用我的測試框架的感覺:這麼單元測試的自動測試發現聲音 矯枉過正例如這裏只有一個 測試功能。相同的代碼可以(應該?)寫入 普通Python中,其基本爲assert

一個顯而易見的選擇是我的代碼更改爲類似

class Test(unittest.TestCase): 
    def one_file(self, filename): 
     # do the actual test 

    def test_file1(self): 
     one_file("first-testcase.foo") 

    def test_file2(self): 
     one_file("second-testcase.foo") 

然後我得到的單元測試的所有優點回來,但是:

  • 這是一個很大更多的代碼寫。

  • 很容易「忘記」測試用例,即在 tests/中創建一個測試文件,並忘記將其添加到Python測試中。

我可以想象一個解決方案,其中我會動態地生成每一個測試用例方法(沿setattr(self, 'test_file' + str(n), ...)的線),以產生用於所述第二溶液中的代碼,而無需手工編寫它。但是這聽起來對於一個看似不那麼複雜的用例來說太過於誇張了。

我怎樣才能獲得最好的兩個,即 自動測試發現(列表tests/*.foo文件),測試 獨立性和正確的報告?

+0

你可以看一下http://pythonhosted.org/behave/。它包含強大的參數化功能。 – Laszlowaty

+0

感謝您的提示,但我不明白這將如何解決我的問題(儘管我可能錯過了某些東西......)。從本質上講,行爲將允許我編寫自然語言而不是Python,並且['Scenario Outlines'](http://pythonhosted.org/behave/tutorial.html#scenario-outlines)將允許因式分解代碼(有點像我的'one_file'函數),但測試套件仍然需要顯式列出所有測試文件,對嗎? –

+0

關於行爲:此外,在這種情況下,我寧願直接編寫比自然語言更多的Python代碼。無論如何,再次感謝這個建議,我正在尋找食物尋找真正的解決方案;-)。 –

回答

3

如果你可以使用pytest爲您的測試運行,那麼這是實際使用parametrize decorator非常簡單:

import pytest, glob 

all_files = glob.glob('some/path/*.foo') 

@pytest.mark.parametrize('filename', all_files) 
def test_one_file(filename): 
    # do the actual test 

這也將自動命名的一個有用的方法測試,這樣就可以看到哪些文件失敗:

$ py.test 
================================== test session starts =================================== 
platform darwin -- Python 3.6.1, pytest-3.1.3, py-1.4.34, pluggy-0.4.0 
[...] 
======================================== FAILURES ======================================== 
_____________________________ test_one_file[some/path/a.foo] _____________________________ 

filename = 'some/path/a.foo' 

    @pytest.mark.parametrize('filename', all_files) 
    def test_one_file(filename): 
>  assert False 
E  assert False 

test_it.py:7: AssertionError 
_____________________________ test_one_file[some/path/b.foo] _____________________________ 

filename = 'some/path/b.foo' 

    @pytest.mark.parametrize('filename', all_files) 
    def test_one_file(filename): 
[...] 
+1

非常好,謝謝。本質上,我缺少的關鍵字是「參數化」(在我的情況下是一個動態參數化),並使用它搜索我發現https://stackoverflow.com/questions/32899/how-to-generate-dynamic-parametrized-單元測試在python這是基本相同的問題。 –

0

這裏是一個解決方案,但它可能會被認爲是不很漂亮......我們的想法是動態創建新的功能,將它們添加到測試類,並使用函數名作爲參數(即克,文件名):

# import 
import unittest 

# test class 
class Test(unittest.TestCase): 

    # example test case 
    def test_default(self): 
     print('test_default') 
     self.assertEqual(2,2) 

# set string for creating new function  
func_string="""def test(cls): 

     # get function name and use it to pass information 
     filename = inspect.stack()[0][3] 

     # print function name for demonstration purposes 
     print(filename) 

     # dummy test for demonstration purposes 
     cls.assertEqual(type(filename),str)""" 

# add new test for each item in list 
for f in ['test_bla','test_blu','test_bli']: 

    # set name of new function 
    name=func_string.replace('test',f) 

    # create new function 
    exec(name) 

    # add new function to test class 
    setattr(Test, f, eval(f)) 

if __name__ == "__main__": 
    unittest.main() 

這正常運行所有四個測試並返回:

> test_bla 
> test_bli 
> test_blu 
> test_default 
> Ran 4 tests in 0.040s 
> OK 
+0

感謝您的回答。這實際上是我的意思,「我可以想象一個解決方案,我可以動態地爲每個測試用例生成一個方法......」。工程,但似乎真的矯枉過正給我。 Freddie關於'parametrize'的回答似乎只對我做了正確的事情。 –

相關問題