<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            16.7. 全部放在一起

            你已經學習了足夠的知識,現在來分析本章樣例代碼的前七行:讀取一個目錄并從中導入選定的模塊。

            例 16.16. regressionTest 函數

            
            def regressionTest():
                path = os.path.abspath(os.path.dirname(sys.argv[0]))   
                files = os.listdir(path)                               
                test = re.compile("test\.py$", re.IGNORECASE)          
                files = filter(test.search, files)                     
                filenameToModuleName = lambda f: os.path.splitext(f)[0]
                moduleNames = map(filenameToModuleName, files)         
                modules = map(__import__, moduleNames)                 
            load = unittest.defaultTestLoader.loadTestsFromModule  
            return unittest.TestSuite(map(load, modules))          
            

            讓我們一行行交互地看。假定當前目錄是 c:\diveintopython\py,其中有包含本章腳本在內的本書眾多樣例。正如在 第 16.2 節 “找到路徑” 中所見,腳本目錄將存于 path 變量,因此讓我們從這里開始以實打實的代碼起步。

            例 16.17. 步驟 1:獲得所有文件

            >>> import sys, os, re, unittest
            >>> path = r'c:\diveintopython\py'
            >>> files = os.listdir(path)                               
            >>> files 1
            ['BaseHTMLProcessor.py', 'LICENSE.txt', 'apihelper.py', 'apihelpertest.py',
            'argecho.py', 'autosize.py', 'builddialectexamples.py', 'dialect.py',
            'fileinfo.py', 'fullpath.py', 'kgptest.py', 'makerealworddoc.py',
            'odbchelper.py', 'odbchelpertest.py', 'parsephone.py', 'piglatin.py',
            'plural.py', 'pluraltest.py', 'pyfontify.py', 'regression.py', 'roman.py', 'romantest.py',
            'uncurly.py', 'unicode2koi8r.py', 'urllister.py', 'kgp', 'plural', 'roman',
            'colorize.py']
            
            1 files 是由腳本所在目錄的所有文件和目錄構成的列表。(如果你已經運行了其中的一些樣例,可能還會看到一些 .pyc 文件。)

            例 16.18. 步驟 2:找到你關注的多個文件

            >>> test = re.compile("test\.py$", re.IGNORECASE)           1
            >>> files = filter(test.search, files)                      2
            >>> files                                                   3
            ['apihelpertest.py', 'kgptest.py', 'odbchelpertest.py', 'pluraltest.py', 'romantest.py']
            
            1 這個正則表達式將匹配以 test.py 結尾的任意字符串。注意,你必須轉義這個點號,因為正則表達式中的點號通常意味著 “匹配任意單字符”,但是你實際上想匹配的事一個真正的點號。
            2 被編譯的正則表達式就像一個函數,因此你可以用它來過濾文件和目錄構成的大列表,找尋符合正則表達式的所有元素。
            3 剩下的是一個單元測試腳本列表,因為只有它們是形如 SOMETHINGtest.py 的文件。

            例 16.19. 步驟 3:映射文件名到模塊名

            >>> filenameToModuleName = lambda f: os.path.splitext(f)[0] 1
            >>> filenameToModuleName('romantest.py')                    2
            'romantest'
            >>> filenameToModuleName('odchelpertest.py')
            'odbchelpertest'
            >>> moduleNames = map(filenameToModuleName, files)          3
            >>> moduleNames                                             4
            ['apihelpertest', 'kgptest', 'odbchelpertest', 'pluraltest', 'romantest']
            
            1 正如你在 第 4.7 節 “使用 lambda 函數” 中所見,lambda 快餐式地創建內聯單行函數。這里應用你在 例 6.17 “分割路徑名” 中已經見過的,標準庫的 os.path.splitext 將一個帶有擴展名的文件名返回成只包含文件名稱的那部分。
            2 filenameToModuleName 是一個函數。lambda 函數并不比你以 def 語句定義的普通函數神奇。你可以如其他函數一樣地調用 filenameToModuleName,它也將如你所愿:從參數中剔除擴展名。
            3 現在你可以通過 map 把這個函數應用于單元測試文件列表中的每一個文件。
            4 結果當然如你所愿:以指代模塊的字符串構成的一個列表。

            例 16.20. 步驟 4:映射模塊名到模塊

            >>> modules = map(__import__, moduleNames)                  1
            >>> modules                                                 2
            [<module 'apihelpertest' from 'apihelpertest.py'>,
            <module 'kgptest' from 'kgptest.py'>,
            <module 'odbchelpertest' from 'odbchelpertest.py'>,
            <module 'pluraltest' from 'pluraltest.py'>,
            <module 'romantest' from 'romantest.py'>]
            >>> modules[-1]                                             3
            <module 'romantest' from 'romantest.py'>
            
            1 正如你在 第 16.6 節 “動態導入模塊” 中所見,你可以通過 map__import__ 的協同工作,將模塊名 (字符串) 映射到實際的模塊 (像其他模塊一樣可以被調用和使用)。
            2 modules 現在是一個模塊列表,其中的模塊和其他模塊一樣。
            3 該列表的最后一個模塊 romantest 模塊,和通過 import romantest 導入的模塊完全等價。

            例 16.21. 步驟 5:將模塊載入測試套件

            >>> load = unittest.defaultTestLoader.loadTestsFromModule  
            >>> map(load, modules)                     1
            [<unittest.TestSuite tests=[
              <unittest.TestSuite tests=[<apihelpertest.BadInput testMethod=testNoObject>]>,
              <unittest.TestSuite tests=[<apihelpertest.KnownValues testMethod=testApiHelper>]>,
              <unittest.TestSuite tests=[
                <apihelpertest.ParamChecks testMethod=testCollapse>, 
                <apihelpertest.ParamChecks testMethod=testSpacing>]>, 
                ...
              ]
            ]
            >>> unittest.TestSuite(map(load, modules)) 2
            
            1 模塊對象的存在,使你不但可以像其他模塊一樣地使用它們;通過類的實例化和函數的調用,你還可以內省模塊,從而弄清楚已經有了那些類和函數。這正是 loadTestsFromModule 方法的工作:內省每一個模塊并為每個模塊返回一個 unittest.TestSuite 對象。每個 TestSuite (測試套件) 對象都包含一個 TestCase 對象的列表,每個對象對應著你的模塊中的一個測試方法。
            2 最后,你將TestSuite列表封裝成一個更大的測試套件。unittest 模塊會很自如地遍歷嵌套于測試套件中的樹狀結構,最后深入到獨立測試方法,一個個加以運行并判斷通過或是失敗。

            自省過程是 unittest 模塊經常為我們做的一項工作。還記得我們的獨立測試模塊僅僅調用了看似神奇的 unittest.main() 函數就大刀闊斧地完成了全部工作嗎?unittest.main() 實際上創建了一個 unittest.TestProgram 的實例,而這個實例實際上創建了一個 unittest.defaultTestLoader 的實例并以調用它的模塊啟動它。 (如果你不給出,如何知道調用它的模塊是哪一個?通過使用同樣神奇的 __import__('__main__') 命令,動態導入正在運行的模塊。我可以就 unittest 模塊中使用的所有技巧和技術寫一本書,但那樣我就沒法寫完這本了。)

            例 16.22. 步驟 6:告知 unittest 使用你的測試套件

            
            if __name__ == "__main__":                   
                unittest.main(defaultTest="regressionTest") 1
            
            1 在不使用 unittest 模塊來為我們做這一切的神奇工作的情況下,你實際上已自己做到了。你已經創建了一個自己就能導入模塊、調用 unittest.defaultTestLoader 并封裝于一個測試套件的 regressionTest 函數。現在你所要做的不是去尋找測試并以通用的方法構建一個測試套件,而是告訴 unittest 前面那些,它將調用 regressionTest 函數,而它會返回可以直接使用的 TestSuite
            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线