天天看點

Python的單元測試(一)

測試驅動的軟體開發方式可以強迫程式員在開發程式的時候使程式的函數之間實作高内聚,低耦合。這樣的方式可以降低函數之間的依賴性,友善後續的修改,增加功能和維護。

說一個函數高内聚,就是指這個函數專注于實作單一的任務,不會做除了生産這個任務以外的其他事情。可以想象一個人,他把自己關在一個小房子裡面生産東西,隻留兩扇窗戶,他需要什麼材料,你就從小窗戶給他送進去(參數),他做好了東西,就給你從另一個窗戶裡面送出來(return),他不會說,我要生産一個輪子,但是我首先需要一個女人進來,他不會說,這是計劃的一部分。

說幾個函數是低耦合的,就是指他們的依賴性小。他們就像是葫蘆娃,每個都有自己獨特的能力,可以自己單幹,在關鍵的時候還可以合體,變成小金剛。他們就像積木一樣,各有各的功能,需要使用的時候直接組合在一起就可以了。

使用測試驅動開發,每一個測試隻測試一個功能,這樣就可以迫使函數把自己獨立出來,盡量減少和其他函數的依賴。

例如,有一個檔案1.txt,他的内容是兩個數字,使用逗号隔開。形如“2,4”(不包括外側雙引号,下同)。我要寫一個程式readandadd.py,讀取硬碟上的1.txt檔案,然後把這個檔案的内容列印到螢幕上。

不規範的寫法一:

f= open('1.txt','r')
    b = f.read().split(',')
    f.close()
    print int(b[0])+int(b[1])      

不規範寫法二:

def A():
        f= open('1.txt','r')
        b = f.read().split(',')
        f.close()
        print int(b[0])+int(b[1])
    A()      

比較規範的寫法:

def read(filename):
        f= open(filename,'r')
        info = f.read()
        f.close()
        return info

    def getnum(info):
        twonum = info.split(',')
        return twonum

    def addnum(twonum):
        return int(twonum[0])+int(twonum[1])

    if __name__ == '__main__':
            info = read('1.txt')
            twonum = getnum(info)
            result = addnum(twonum)
            print result      

這樣寫的好處是,如果想測試讀檔案的功能,就隻需要測試read()函數,如果想測試把兩個數分開的功能,就隻需要測試getnum()函數。而相反,在不規範寫法二中,雖然隻想測試兩個數字相加的功能,可是卻不得不首先打開檔案并讀取檔案然後把數字分開。

繼續回到比較規範的寫法當中,我相信很多人寫完read()函數以後,肯定會輸入如下代碼:

def read(filename):
        f= open(filename,'r')
        info = f.read()
        f.close()
        return info

    print read('1.txt')      

然後運作程式,發現正常列印出’2,3’以後,再開始寫getnum()函數。寫完getnum以後,測試getnum()函數沒問題以後再開始寫然後測試addnum()函數。最後測試整個程式的功能。

其實這個過程,已經就是在做單元測試了。然而這樣操作的弊端是什麼?如果整體程式已經寫好了,之前做測試點代碼也就删除了。那麼如果突然把程式做了修改。例如1.txt裡面數字的分隔從1個逗号變成了空格,或者變成了3個數字,那必然要修改getnum(),但是又如何測試修改的部分呢?還要把不相幹的代碼給注釋掉。不僅麻煩,而且容易出錯。

現在,把測試的代碼單獨獨立出來。會有什麼效果呢?嘗試建立一個test.py程式,代碼如下:

import readandadd

    def testread():
        print 'read:',readandadd.read('1.txt')

    def testgetnum():
        print 'getnum:',readandadd.getnum('2,3')

    def testaddnum():
        print 'addnum:',readandadd.addnum([2,3])

    if __name__ == '__main__':
        testread()
        testgetnum()
        testaddnum()      

運作test.py以後輸出結果如下:

read: 2,3
    getnum: ['2', '3']
    addnum: 5      

每一個函數的輸出結果一目了然,而且在修改了readandadd.py的函數以後,重新運作test.py就可以知道輸出結果有沒有符合預期。

當然,這裡這個例子非常的簡單,是以可以人工通過觀察test.py的輸出結果來确定是否符合預期,那對于大量的函數的測試,難道也要讓肉眼來看嗎?當然不是。于是,下一篇文章将會介紹Python的單元測試unittest。

繼續閱讀