瘋狂Python講義學習筆記(含習題)之文檔和測試


軟件測試的目的是爲了找出軟件系統中存在的缺陷,然後將這些缺陷提交給Bug管理系統(如Bugzilla等)。

一、使用pydoc生成文檔

藉助於Python自帶的pydoc模塊,可以非常方便地查看、生成幫助文檔,該文檔是HTML格式的,因此查看、使用起來都非常方便。

MY_NAME = '冰藍工作室'


def say_hi(name):
    '''
    定義一個打招呼的函數
    返回對指定用戶打招呼的字符串
    '''
    print("執行say_hi函數")
    return name + '您好!'


def print_rect(height, width):
    '''
    定義一個打印矩形的函數
    :param heiht: 代表矩形的高
    :param width: 代表矩形的寬
    '''
    print(('*' * width + '\n') * height)


class User:
    NATIONAL = 'China'
    '''
    定義一個代表用戶的類
    該類包括name、age兩個變量
    '''

    def __init__(self, name, age):
        '''
        name初始化該用戶的name
        age初始化該用戶的age
        '''
        self.name = name
        self.age = age

    def eat(food):
        '''
        定義用戶喫東西方法。
        food——代表正在喫的東西
        '''
        print('{0}正在喫{1}'.format(self.name, food))

(一)在控制檯中查看文檔

使用pydoc模塊在控制檯中查看幫助文檔的命令如下:
python -m pydoc 模塊名
其中-m是python命令的一個選項,表示運行指定模塊,此處表示運行pydoc模塊。
在代碼目錄下運行命令 python -m pydoc ibmodule得到如下圖輸出結果:

在這裏插入圖片描述
pydoc文檔總是按如下順序來顯示模塊中的全部內容

  • 模塊的文檔說明:就是*.py文件頂部的註釋信息,這部分信息會被提取成模塊的文檔說明。也就是上圖中①的部分
  • CLASS部分:這部分會列出該模塊所包含的全部類。也就是上圖中②的部分
  • FUNCTIONS部分:這部分會列出該模塊所包含的全部函數。也就是上圖中③的部分
  • DATA部分:這部分會列出該模塊所包含的全部成員變量。也就是上圖中④的部分
  • FILE部分:這部分會顯示該模塊對應的源文件位置。也就是上圖中⑤的部分

(二)生成HTML文檔

使用pydoc模塊生成HTML幫助文檔的命令如下:
python -m pydoc -w 模塊名
以上命令中額外指定了-w選項,該選項代表write,表明輸出HTML文檔。
在這裏插入圖片描述
可以看到,在模塊所在目錄下生成了一個ibmodule.html文件,該文件更加清晰的描述了模塊的幫助信息。
在這裏插入圖片描述
pydoc還可用於爲指定目錄生成HTML文檔。如:python -m pydoc -w 目錄名

(三)啓動本地服務器來查看文檔信息

python -m pydoc -p 端口號
以上命令在指定端口啓動HTTP服務器,接下來用戶可以通過瀏覽器來查看python的所有模塊的文檔信息。

(四)查找模塊

pydoc還提供了一個-k選項,該選項用於查找模塊。
python -m pydoc -k 被搜索模塊的部分內容

二、軟件測試概述

軟件測試是保證軟件質量的重要手段之一。

(一)軟件測試的概念和目的

IEEE定義:測試是使用人工和自動手段來運行或檢測某個系統的過程,其目的在於檢驗系統是否滿足規定的需求,或者弄清預期結果與實際結果之間的差別。
軟件測試基本原則:

  • 應該儘早並不斷地進行軟件測試。
  • 測試用例應該由測試輸入數據和對應的預期輸出結果兩部分組成
  • 開發人員避免測試自己的程序
  • 在設計測試用例時,至少應該包括合理的輸入和不合理的輸入兩種
  • 應該充分注意測試中的羣集現象,經驗表明,測試後程序中殘存的錯誤數目與該程序中已發現的錯誤數呈正比
  • 嚴格執行測試計劃,避免測試的隨意性
  • 應當對每一個測試結果都做全面檢查
  • 妥善保存測試計劃、測試用例、出錯統計和最終分析報告,爲維護提供方便

(二)軟件測試的分類

從軟件工程的總體把握來分,軟件測試可以分爲:

  • 靜態測試:針對測試不運行部分進行檢查和審閱。靜態測試又分爲三類:
    • 代碼審閱:檢查代碼涉及的一致性,檢查代碼的標誌性、可讀性,包括代碼結構的合理性。
    • 代碼分析:主要針對程序進行流程分析,包括數據流分析、接口分析和表達式分析等。
    • 文檔檢查:主要檢查各階段的文檔是否完備。
  • 動態測試:通過運行和試探發現缺陷。動態測試又分爲兩類:
    • 結構測試(白盒):各種覆蓋測試。
    • 功能測試(黑盒):集成測試、系統測試和用戶測試等。

從軟件測試工程的大小來分,軟件測試又可分爲如下類別:

  • 單元測試:測試中的最小單位,測試特殊的功能和代碼模塊。通常由開發者完成。
  • 集成測試:測試應用程序結合的部分來確定他們的功能是否正確,主要測試功能模塊或獨立的應用程序。
  • 系統測試:典型的黑盒測試,與應用程序的功能需求緊密相關。
  • 用戶測試:分爲α和β測試,α測試由測試人員模擬最終用戶來完成。β測試由最終用戶來完成,在實際使用環境中試用。
  • 平行測試:同時運行新開發的系統和即將取代的舊系統,比較新舊兩個系統的運行結果。

(三)開發活動和測試活動

軟件開發常規流程:

  1. 軟件開發人員發佈一個新功能、新模塊,隨之一起提交的還有軟件開發文檔、發佈文檔(包括新增了哪些功能、改進了哪些功能),以及軟件安裝、部署等相關文檔。
  2. 軟件測試人員按照軟件開發人員提供的文檔來安裝、部署新功能、新模塊。
  3. 軟件測試人員準備進行測試,在測試之前要編寫詳細的測試計劃並準備測試用例。
  4. 軟件測試人員進行實際測試,並編寫一些自動化測試腳本,以簡化下一次的迴歸測試,然後將測試發現的Bug提交到Bug管理系統(如Bugzilla) 。
  5. 軟件開發人員查看Bug管理系統,對軟件測試人員提交的Bug進行修復,提交所做的修改,並在Bug管理系統中將該Bug設爲己修復。
  6. 軟件測試人員針對己修復的Bug進行迴歸測試。如果Bug依然存在,則在Bug管理系統中將Bug重新打開(即將其設爲需要修復的Bug) 。
  7. 重複5、6兩個步驟,知道測試沒有發現Bug爲止。

(四)常見的Bug管理工具

主流Bug管理工具:

  • Bugzilla:一個開源、免費且功能強大的Bug管理工具,只有在初次使用時其配置、上手稍微複雜一點。可以與CVS進行整合開發。
  • BugFree:一個開源、免費的Bug管理工具,它是一款B/S結構的Bug管理軟件。
  • TestDirector:由Mercury Interactive 公司(著名的軟件測試工具提供商,現己被惠普收購)開發的一款商業的軟件測試管理工具,也是業界第一個基於Web 的測試管理系統,從而允許軟件開發和軟件測試人員既可在公司內部,也可在公司外部進行測試管理。
  • JIRA:由Atlassian公司提供的一款集項目計劃、任務分配、需求管理、Bug跟蹤於一體的商業軟件。
  • ClearQuest:IBM 的Rational 旗下的一個專業的Bug 管理工具,它可以對Bug 和記錄的變化進行跟蹤管理,也可以跟蹤一個Bug 完整的生命週期,從提交到關閉, ClearQuest 可以記錄Bug 所有的改變歷史。同時ClearQuest 提供了各種方便的查詢功能,從而可以及時瞭解每個Bug 的處理情況。
  • MantisBT:一個基於PHP + MySQL (或SQL Server 、PostgreSQL )技術開發的B/S 結構的Bug 管理系統,開源、免費。

三、文檔測試

所謂文檔測試,指的是通過doctest模塊運行Python源文件的說明文檔中的測試用例,從而生成測試報告。
文檔測試工具可以提取說明文檔中的測試用例,其中“>>>”之後的內容表示測試用例, 接下來的一行則代表該測試用例的輸出結果。文檔測試工具會判斷測試用例的運行結果與輸出結果是否一致,如果不一致就會顯示錯誤信息。

def square(x):
    '''
    一個用於計算平方的函數
    :param x: 待計算的數
    :return: 計算結果
    例如:
    >>> square(2)
    4
    >>> square(3)
    9
    >>> square(-3)
    9
    >>> square(0)
    0
    '''
    return x * 2 # 故意寫錯


class User:
    '''
    定義一個代表用戶的類,該類包含如下兩個屬性
    name:代表用戶的名字
    age:代表用戶的年齡

    例如:
    >>> u = User('ib-top', 9)
    >>> u.name
    'ib-top'
    >>> u.age
    9
    >>> u.say('i love python')
    'ib-top說:i love python'
    '''

    def __init__(self, name, age):
        self.name = 'ib_top' # 故意寫錯
        self.age = age

    def say(self, content):
        return self.name + '說:' + content


if __name__ == '__main__':
    import doctest
    doctest.testmod()

運行結果:
在這裏插入圖片描述
每個測試用例結果都包含如下4個部分:

  • 第一部分:顯示在哪個源文件的哪一行
  • 第二部分:Failed example,顯示是哪個測試用例出錯了
  • 第三部分:Expected,顯示程序期望的輸出結果
  • 第四部分:Got,顯示程序實際運行產生的輸出結果

四、單元測試

(一)單元測試概述

單元測試是一種小粒度的測試,用以測試某個功能或代碼塊。
好處:

  • 提高開發速度:藉助專業框架,能以自動化方式執行,從而提高開發者開發、測試的執行效率。
  • 提高軟件代碼質量:使用小版本發佈、集成,有利於開發人員實時除錯,同時引入重構,使代碼具有可擴展性。
  • 提升系統的可信賴度:作爲一種迴歸測試,支持在修復或更正後進行“再測試”,保證代碼的正確性。

被測對象:

  • 結構化編程語言中的函數
  • 面向對象編程語言中的接口、類、對象

任務:

  • 被測單元的接口測試
  • 被測單元的局部數據結構測試
  • 被測單元的邊界條件測試
  • 被測單元中的所有獨立執行路徑測試
  • 被測單元中的各條錯誤處理路徑測試

被測單元的接口測試指的是它與其他程序單元的通信接口,如調用方法的方法名、形參等。
接口測試是單元測試的基礎,只有在數據能正確輸入、輸出的前提下,其他測試纔有意義。
接口測試考慮因素:

  • 輸入的實參與形參的個數、類型是否匹配
  • 調用其他程序單元(如方法)時所傳入實參的個數、類型與被調用程序單元的形參的個數、類型是否匹配
  • 是否存在與當前入口點無關的參數引用
  • 是否修改了只讀型參數
  • 是否把某些約束也作爲參數傳入了

如果被測程序單元還包含來自外部的輸入/輸出,則還應考慮如下因素:

  • 打開的輸入/輸出流是否正確
  • 是否正常打開、關閉了輸入/輸出流
  • 格式說明與輸入/輸出語句是否匹配
  • 緩衝區大小與記錄長度是否匹配
  • 文件在使用前是否已經打開
  • 是否處理了文件尾
  • 是否處理了輸入/輸出錯誤

局部變量往往是錯誤的根源,應仔細設計測試用例,確保能發現以下錯誤:

  • 不合適或不兼容的類型聲明
  • 沒有爲局部變量指定初值
  • 局部變量的初值或默認值有錯
  • 局部變量名出錯(包括手誤拼錯或不正確的截斷)
  • 出現上溢、下溢和地址異常

除此之外,如果被測程序單元還與程序中的全局變量耦合,那麼在進行單元測試時還應該查清全局變量對被測單元的影響。
軟件測試還應着重檢查一下問題:

  • 輸出的出錯信息是否易於理解、調試
  • 記錄的錯誤信息與實際遇到的錯誤是否相符
  • 異常處理是否合適
  • 錯誤信息中是否包含足夠的出錯定位信息

(二)單元測試的邏輯覆蓋

單元測試用例設計最基本、最簡單的方法就是邊界值分析。指測試經驗表明大量的錯誤都發生在輸入/輸出範圍的邊界條件上,而不是某個範圍內部。
單元測試的邏輯覆蓋包括:

  • 語句覆蓋——每條語句都至少執行一次
  • 判定(邊)覆蓋——每條語句都執行,每個判定的所有可能結果都至少執行一次
  • 條件覆蓋——每條語句都執行,判定表達式的每種可能都取得各種結果
  • 判定-條件覆蓋——同時滿足判定覆蓋和條件覆蓋,每個判定條件的各種可能組合都至少出現一次
  • 路徑覆蓋——程序的每條可能路徑都至少執行一次

1. 語句覆蓋

設計若干測試用例,執行被測程序,是的每條可執行語句都至少執行一次。語句覆蓋是最弱的邏輯覆蓋測試

def test(a, b, m):
    if a > 10 and b == 0:
        m = a + b
    if a == 20 or m > 15:
        m += 1

以上代碼的執行流程如下:

對於以上代碼,可設計如下測試用例:

  • 用例1:a=20; b=0; m=3,程序按路徑ACE執行,該代碼的4條語句均得到執行,從而實現語句覆蓋
  • 用例2:a=20; b=1; m=3,程序按路徑ABE執行,沒有做到語句覆蓋,m = a + b沒有被執行。

2. 判定(邊)覆蓋

設計若干測試用例,執行被測程序,使得程序中每個判斷的取真分支和取假分支都至少執行一次,即判斷真假值均得到滿足,因此又被稱爲分支覆蓋。

測試用例 a,b,m取值 通過路徑
用例1 20,0,3 ACE
用例2 10,0,3 ABD
用例3 12,0,3 ACD
用例4 20,1,1 ABE

3. 條件覆蓋

設計若干測試用例,執行被測試程序,使得每個判斷中每個條件的可能取值都至少滿足一次。
前面代碼中一共包含以下幾種情況:
(1) a>10,取真值,記爲T1
(2) a>10,取假值,即a <= 10,記爲F1
(3) b0,取真值,記爲T2
(4) b
0,取假值,即b != 0,記爲F2
(5) a20,取真值,記爲T3
(6) a
20,取假值,即a != 20,記爲F3
(7) m>15,取真值,記爲T4
(8)m>15,取假值,即m <= 15,記爲F4

測試用例 a,b,m取值 通過路徑 覆蓋條件
用例1 20,0,1 ACE T1, T2, T3, T4
用例2 5,0,2 ABD F1,T2,F3,F4
用例3 20,1,5 ABE T1,F2,T3,F4

3 個測試用例覆蓋了4 個條件的8種情況。進一步分析發現, 3個測試用例也把兩個判斷的4個分支B 、C 、D 和E都覆蓋了。但滿足條件覆蓋的測試用例不一定滿足判定覆蓋。

4. 判定-條件覆蓋

求設計足夠多的測試用例,使得判斷中每個條件所有可能的組合都至少出現一次,並且每個判斷本身的判定結果也至少出現一次。

測試用例 a,b,m取值 通過路徑 覆蓋條件
用例1 20,0,3 ACE T1, T2, T3, T4
用例2 20,1,10 ABC T1,T2,T3,F4
用例3 10,0,20 ABE F1,T2,F3,T4
用例4 1,2,3 ABD F1,F2,F3,F4

5. 路徑覆蓋

設計足夠多的測試用例,覆蓋程序中所有可能的路徑。

測試用例 a,b,m取值 通過路徑
用例1 20,0,3 ACE
用例2 5,0,2 ABD
用例3 20,1,10 ABE
用例4 12,0,7 ACD

測試的目的不是要證明程序的正確性,而是要儘可能找出程序中的缺陷。沒有完備的測試方法,也就沒有完備的測試活動。

五、使用PyUnit(unittest)

PyUnit是Python自帶的單元測試框架,用於編寫和運行可重複的測試。PyUnit 是xUnit 體系的一個成員, xUnit 是衆多測試框架的總稱,PyUnit 主要用於進行白盒測試和迴歸測試。
好處:

  • 可以使測試代碼與產品代碼分離
  • 針對某一個類的測試代碼只需要進行較少的改動,便可以應用於另一個類的測試。
  • 開放源代碼,可以進行二次開發,方便擴展

特徵:

  • 使用斷言方法判斷期望值和實際值的差異,返回bool值
  • 測試驅動設備可使用共同的初始化變量和實例
  • 測試包結構便於組織和集成運行

(一)PyUnit(unittest)的用法

測試的本質:通過給定參數來執行函數,然後判斷函數的實際輸出結果和期望輸出結果是否一致。
PyUnit基於斷言機制來判斷函數或方法的實際輸出結果和期望輸出結果是否一致。
測試驅動開發方式強調先編寫測試用例,然後再編寫函數和方法。
實例:

def one_equation(a, b):
    '''
    求一元一次方程a * x + b = 0的解
    :param a: 方程中變量的係數
    :param b: 房中的常量
    :return: 方程的解
    '''
    # 如果a = 0,則方程無法求解
    if a == 0:
        raise ValueError("參數錯誤")
    # 返回方程的解
    else:
        return -b / a


def two_equation(a, b, c):
    '''
    求一元二次方程a * x * x + b * x + c = 0的解
    :param a: 方程中變量二次冪的係數
    :param b: 方程中變量的係數
    :param c: 方程中的常量
    :return: 方程的解
    '''
    # 如果 a == 0 ,則變成一元一次方程
    if a == 0:
        raise ValueError("參數錯誤")
    # 在有理數範圍內無解
    elif b * b - 4 * a * c < 0:
        raise ValueError("方程在有理數範圍內無解")
    # 方程有唯一的解
    elif b * b - 4 * a * c == 0:
        # 使用數組返回方程的解
        return -b / (2 * a)
    # 方程有兩個解
    else:
        r1 = (-b + (b * b - 4 * a * c)**0.5) / 2 / a
        r2 = (-b - (b * b - 4 * a * c)**0.5) / 2 / a
        return r1, r2

定義好以上程序之後,該程序就相當於一個模塊,接下來就可以爲該模塊編寫單元測試代碼了。
unittest要求單元測試類必須繼承unittest.TestCase,該類中的測試方法滿足如下要求:

  • 測試方法應該沒有返回值
  • 測試方法不應該有任何參數
  • 測試方法應以test_開頭

下面是測試用例的代碼:

import unittest
from tb_math import *


class TestIbModule(unittest.TestCase):
    # 測試一元一次方程的解
    def test_one_equation(self):
        # 斷言該方程的解爲-1.8
        self.assertEqual(one_equation(5, 9), -1.8)
        # 斷言該方程的解應該爲-2.5
        self.assertTrue(one_equation(4, 10) == -2.5, .00001)
        # 斷言該方程的姐應該爲27/4
        self.assertTrue(one_equation(4, -27) == 27 / 4)
        # 斷言當 a == 0時的情況,斷言引發ValueError
        with self.assertRaises(ValueError):
            one_equation(0, 9)

    # 測試一元二次方程的解
    def test_twoequation(self):
        r1, r2 = two_equation(1, -3, 2)
        self.assertCountEqual((r1, r2), (1.0, 2.0), '求解出錯')
        r1, r2 = two_equation(2, -7, 6)
        self.assertCountEqual((r1, r2), (1.5, 2.0), '求解出錯')
        # 斷言只有一個解的情況
        r = two_equation(1, -4, 4)
        self.assertEqual(r, 2.0, '求解出錯')
        # 斷言當 a == 0 時的情況,斷言引發ValueError
        with self.assertRaises(ValueError):
            two_equation(0, 9, 3)
        # 斷言引發ValueError
        with self.assertRaises(ValueError):
            two_equation(4, 2, 3)

以上代碼中使用斷言方法判斷函數的實際輸出結果與期望結果是否一致,如果一致則表明測試通過,否則表明測試失敗。

在測試某個方法時,如果實際測試要求達到某種覆蓋程度,那麼在編寫測試用例時必須傳入多組參數來進行測試,使得測試能達到指定的邏輯覆蓋。

** TestCast中常用的斷言方法**

斷言方法 檢查條件
assertEqual(a, b) a == b
assertNotEqueal(a, b) a != b
assertTrue(x) bool(x) is True
asseretFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not instance(a, b)

** TestCase包含的與異常、錯誤、警告和日誌相關的斷言方法 **

斷言方法 檢查條件
assertRaises(exc, fun, *args, **kwds) fun(*args, **kwads)引發exc異常
assertRaisesRegex(exc, r, fun, *args, **kwds) fun(*args, **kwds)引發exc異常,且異常信息匹配r正則表達式
assertWarns(warn, fun, *args, **kwds) fun(*args, **kwds)引發warn警告
assertWransRegex(warn, r, fun, *args, **kwds) fun(*args, **kwds)引發wran警告,且警告信息匹配r正則表達式
assertLogs(logger, level) With語句塊使用日誌器生成level級別的日誌

** TestCase包含的用於完成某種特定檢查的斷言方法 **

斷言方法 檢查條件
assertAlmostEqual(a, b) round(a-b, 7) == 0
assertNotAlmostEqual(a, b) round(a-b, 7) !=0
assertGreater(a, b) a > b
assrtGreaterEqual(a, b) a >=b
assertLess(a, b) a < b
assertLessEqual(a, b) a <= b
assertRegex(s, r) r.search(s)
assertNotRegex(s, r) not r.search(s)
assertCountEqual(a, b) a、b兩個序列包含的元素相同,不管元素出現的順序如何

** TestCase包含的針對特定類型的斷言方法**

斷言方法 用於比較的類型
assertMultiLineEqual(a, b) 字符串(string)
assertSequenceEqual(a, b) 序列(sequence)
assertListEqual(a, b) 列表(list)
assertTupleEqual(a, b) 元組(tuple)
assertSetEqual(a, b) 集合(set或frozenset)
assertDictEqual(a, b) 字典(dict)

(二)運行測試

(1) 通過代碼調用測試用例。程序可以通過調用unittest.main()來運行當前源文件中的所有測試用例。

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

(2) 使用unittest模塊運行測試用例。

python -m unittest 測試文件

(三)使用測試包

使用測試包(TestSuite)可以組織多個測試用例,測試包還可以嵌套測試包。在使用測試包組織多個測試用例和測試包之後,程序可以使用測試運行期(TestRunner)來運行該測試包所包含的所有測試用例。
假設有一個hello.py程序

# 該方法簡單地返回字符串
def say_hello():
    return "Hello World."


# 計算兩個整數的和
def add(nA, nB):
    return nA + nB

接下來編寫一個測試類

import unittest
from hello import *


class TestHello(unittest.TestCase):
    # 測試say_hello函數
    def test_say_hello(self):
        self.assertEqual(say_hello(), "Hello World.")

    # 測試add函數
    def test_add(self):
        self.assertEqual(add(3, 4), 7)
        self.assertEqual(add(0, 4), 4)
        self.assertEqual(add(-3, 0), -3)

然後就可以通過TestSuite將前面的測試一元二次方程的測試用例和這個測試用例組織到一起,然後使用TestRunner來運行測試包了。

import unittest
from testuni_test_math import TestIbModule
from test_hello import TestHello

test_case = (TestHello, TestIbModule)


def whole_suite():
    # 創建測試加載數據
    loader = unittest.TestLoader()
    # 創建測試包
    suite = unittest.TestSuite()
    # 遍歷所有測試類
    for test_class in test_case:
        # 從測試類中加載測試用例
        tests = loader.loadTestsFromTestCase(test_class)
        # 將測試用例添加到測試包中
        suite.addTests(tests)
    return suite


if __name__ == '__main__':
    # 創建測試運行期(TestRunner)
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(whole_suite())

(四)測試固件之setUp和tearDown

測試固件:代表執行一個或多個測試用例所需的準備工作,以及相關聯的準備操作,準備工作可能包括穿件臨時數據庫、創建目錄、開啓服務器進程等。
unittest.TestCase包含了setUp()和tearDown()兩個方法,其中setUp()方法用於初始化測試固件;而tearDown()用於銷燬測試固件。程序會在運行每個測試用例(以test_開頭的方法)之前自動執行setUp()方法來初始化測試固件,並在每個測試用例運行完成之後自動執行tearDwon()方法來銷燬測試固件。

(五)跳過測試用例

(1) 使用skipXxx裝飾器來跳過測試用例。unittest一共提供了3個裝飾器,分別是@unittest.skip(reason)、@unittest.skipIf(condition, reason)和@unittest.skipUnless(condition, reason)。其中skip代表無條件跳過,skipIf代表當condition爲True時跳過;skipUnless代表當condition爲False時跳過。
(2) 使用TestCase的skipTest()方法來跳過測試用例。

習題

  1. 定義一個包,該包包含兩個模塊,在每個模塊下定義兩個函數和一個類,程序爲這個包及其所包含的其他程序單元編寫文檔說明,併爲包生成HTML文檔。
    ① 新建test_package文件夾
    ② 在該文件夾下創建test_module1.py、test_module2.py兩個文件
    test_module1.py
# -*- encoding: utf-8 -*-
"""
# @Time : 2020/1/1 19:08
# @Author : 冰藍工作室
# @File : test_module1.py
# @Contact : [email protected]
# @License : (C)Copyright 2019, www.ib-top.com
# @Desc : None
"""


class Test_Class1:
    '''
    一個用來測試文檔編寫的類
    該類包括name、age兩個屬性
    '''

    def __init__(self, name, age):
        '''
        :param name: 初始化測試類的name屬性
        :param age: 初始化測試類的age屬性
        '''
        self.name = name
        self.age = age

    def run(self):
        '''
        定義奔跑的方法
        '''
        print('{0}正在奔跑!'.format(self.name))


def test_function1():
    '''
    一個測試文檔編寫的函數
    該函數打印自己的名字
    '''
    print("正在執行test_module1中的函數test_function1")


def test_function2(name, age):
    '''
    另一個測試文檔編寫的函數
    :param name: 代表姓名
    :param age: 代表年齡
    :return: 返回對指定用戶打招呼的字符串
    '''
    return name + "您好!您的年齡是" + age

testmodule2.py

# -*- encoding: utf-8 -*-
"""
# @Time : 2020/1/1 19:39
# @Author : 冰藍工作室
# @File : test_module2.py
# @Contact : [email protected]
# @License : (C)Copyright 2019, www.ib-top.com
# @Desc : None
"""


class Test_class2:
    '''
    一個用來測試文檔編寫的類
    該類包含name,age兩個屬性
    '''

    def __init__(self, name, age):
        '''
        類的初始化方法
        :param name: 初始化name屬性
        :param age: 初始化age屬性
        '''
        self.name = name
        self.age = age

    def sayHi(self):
        '''
        定義說話的方法
        :return: 說話的字符串
        '''
        return self.name + "說:我的年齡是" + self.age + "歲!"


def test_function1():
    '''
    模塊test_module2的文檔編寫測試函數
    該函數打印自己的函數名
    '''
    print("正在執行test_module2中的函數test_function1")


def test_function2():
    '''
    模塊test_module2的文檔編寫測試函數
    :return: 該函數返回自己的函數名
    '''
    return "test_module2中函數test_function2"

③使用pydoc模塊生成HTML文檔
從命令行進入test_package目錄的上級目錄,使用命令 python -m pydoc -w test_package.test_module1爲第一個模塊生成HTML文檔,使用命令python -m pydoc -w test_package.test_module2爲第二個模塊生成HTML文檔,使用命令python -m pydoc -w test_package爲包生成HTML文檔。
命令執行完後的目錄結構爲:

test_package
test_package.html
test_package.test_module1.html
test_package.test_module2.html
  1. 定義三個函數和一個類,爲這些函數和類提供文檔測試,並運行文檔測試。
def add(x, y):
    '''
    一個用於計算加法的函數
    :param x: 加數
    :param y: 被加數
    :return: 兩個數的和
    例如:
    >>> add(1, 2)
    3
    >>> add(1.1, 2)
    3.1
    >>> add(-1, 1)
    0
    >>> add(0, 1)
    1
    '''
    return x + y


def cube(x):
    '''
    計算一個數的立方
    :param x: 待計算的數
    :return: 計算結果
    例如:
    >>> cube(3)
    27
    >>> cube(2)
    8
    >>> cube(-2)
    -8
    >>> cube(1.1)
    1.3310000000000004
    >>> cube(0)
    0
    '''
    return x**3


def sayHi():
    '''
    打印固定字符串
    例如:
    >>> sayHi()
    Hello World!
    '''
    print('Hello World!')


class Person:
    '''
    定義一個代表人的類,該類包含如下兩個屬性
    name: 代表姓名
    age: 代表年齡

    例如:
    >>> p = Person('ib-top', 20)
    >>> p.name
    'ib-top'
    >>> p.age
    20
    >>> p.say('i love python')
    'ib-top說:i love python'
    '''

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self, content):
        return self.name + '說:' + content


if __name__ == '__main__':
    import doctest
    doctest.testmod()

  1. 定義三個函數和一個類,爲這些函數和類提供單元測試,並運行單元測試。
import unittest


def add(x, y):
    '''
    計算兩個數的和
    :param x: 加數
    :param y: 被加數
    :return: 兩個數的和
    '''
    return a + b


def square(x):
    '''
    計算一個數的平方
    :param x: 待計算平方的數
    :return: 計算結果
    '''
    return x**2


def say():
    '''
    打印字符串Hello World!
    '''
    print('Hello World!')


class Person:
    '''
    定義代表人的類,包含兩個屬性name和age
    '''

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sayHi(self):
        return self.name + '說:我今年' + self.age + '歲!'


# 測試用例
class TestSequenceFunctions(unittest.TestCase):
    # 測試加法函數
    def test_add(self):
        # 斷言結果爲3
        self.assertEqual(add(1, 2), 3)
        # 斷言結果爲-1
        self.assertTrue(add(-2, 1) == -1, .00001)
        # 斷言結果爲0
        self.assertTrue(add(0, 0) == 0)

    # 測試平方函數
    def test_square(self):
        # 斷言結果爲4
        self.assertEqual(square(2), 4)
        # 斷言結果爲1
        self.assertTrue(square(-1) == 1)
        # 斷言結果爲0
        self.assertEqual(square(0), 0)

    # 測試打印函數
    def test_say(self):
        # 斷言結果爲Hello World!
        self.assertEqual(say(), 'Hello World!')

    def setUp(self):
        self.man = Person('ib-top', 29)
        print('set up now')

    def test1(self):
        self.assertEqual(self.man.name, 'ib-top')

    def test2(self):
        self.assertEqual(self.man.age, 29)

    def test3(self):
        self.assertEqual(self.man.sayHi(), 'ib-top說:我今年29歲!')


if __name__ == '__main__':
    unittest.main()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章