測試理論回顧:
黑盒測試:是針對整個産品系統進行的測試,驗證系統是否滿足需求規格的定義,及軟體産品的正确性和性能等是否滿足其需求規格的要求。
灰盒測試:是介于白盒測試與黑盒測試之間的一種測試,灰盒測試多用于內建測試階段,不僅關注輸出、輸入的正确性,同時也關注程式内部的情況。
白盒測試:是通過程式的源代碼進行測試而不使用使用者界面。這種類型的測試需要從代碼内部在算法,路徑,條件等等中的缺點或者錯誤,進而加以修正。
單元測試:
單元測試是針對程式的最小單元來進行正确性檢驗的過程。單元:一個單元可能是單個程式、類、對象、方法(函數)等。 優點是可以減少BUG;快速定位BUG;提高代碼品質;減少調試時間。缺點是周期時間長;耗費資源(主要是人力資源);能力要求高。
一、單元測試流程
1. 單元測試-計劃:1) 确定要測試代碼範圍;2) 評估标準(确定被測代碼的覆寫率)
2. 測試政策-設計:1) 拿到開發代碼進行調整(保證單元間可獨立執行)
3. 測試政策-實作:1) 根據調整好的代碼-畫流程圖;2) 根據流程圖畫流圖-确定複雜度、路徑;3) 根據複雜度和路徑确定測試用例(測試資料)
4. 單元測試-執行:1) 使用測試架構(UnitTest)編寫單元測試用例;2) 測試用例(代碼)和測試資料分離;3) 生成測試報告
二、單元測試-計劃
确定要測試代碼以及确定這些被測代碼的評估标準、優先級等說明:1. 确定單元測試範圍(哪些代碼要做單元測試);評估标準-(被測代碼的邏輯覆寫率)
1.1、如何确定20%代碼
确定單元測試的代碼測試範圍,我們依據:二八原則(20%的代碼中隐藏了80%的缺陷)
1. 頻率:使用頻率高的代碼段;
2. 複用性:(是否已被複用,是否被别的地方引用,如果被别的地方調用這個函數,沒問題,基本不用測)
1). 全新(指沒有被複用的代碼)
2). 部分複用
3). 全部複用
3. 開發人員:
1). 技術(指由技術水準不那麼高的開發人員寫的代碼)
2). 業務(指由對業務不那麼熟悉的開發人員寫的代碼)
4. 複雜度:業務邏輯複雜度(一般認為圈複雜度在7級以上代碼包括在20%的代碼中)
最後,我們會得到一張表,來列名我們要測的代碼範圍:

測試範圍
1.2、評估标準(覆寫率)如何确定邏輯覆寫率
例如,我們拿到一段代碼,實作的一個需求,我們首先要畫流程圖(使用統一規定标準圖形,描述程式運作的具體步驟),然後以此來确定覆寫率,并且以後可以根據流程圖畫流圖 1. 語句覆寫率,說明:非分支非判斷的代碼,覆寫率計算方式:被覆寫語句/總語句,例如,流程圖裡有3個語句,對于一個測試資料,它能覆寫2個語句,則語句覆寫率為2/3
2. 分支覆寫率,說明:判斷語句的分支,例如if判斷有兩個分支,覆寫率計算方式:覆寫分支/總分支數,例如流程圖裡的if判斷有2個分支,對于一個測試資料,它能覆寫1個分支,則分支覆寫率為1/2
3. 條件覆寫率,說明:一個條件有兩個結果 true、false,是以一個條件分母為2,兩個條件分母為4...例如:if username=='admin' and pwd=='123456':這裡就有兩個條件,分母為4
注意:條件之間使用邏輯符 and連接配接,第一個條件如果失敗,不會在去判斷第二個條件,如果測試資料是username==ad, pwd就不會判斷,覆寫率為1/4;如果為or第一個條件失敗回去判斷第二條件
4. 路徑覆寫率,說明:路徑就是從開始-到結束的過程, 覆寫率計算方式:覆寫路徑/全部路徑
注意:路徑的分子永遠為1, 有時候看流程圖路徑有4條,但有一條永遠不可成立,是以實際路徑為3條,計算路徑覆寫率時分母就為3
5. 分支條件覆寫率,說明:分支和條件的組合,1.分子=分支覆寫率的分子+條件覆寫率的分子;2.分母=分支覆寫率的分母+條件覆寫率的分母
三、單元測試政策-設計
單元測試政策:針對單元測試選擇測試的一種方式;
單元測試政策方式:
1. 自上到下,方式:從最上層函數往下開始逐層測試,缺點(成本高)
2. 自下到上,方式:從最底層函數往上開始逐層測試,缺點(周期長,需要開發寫完所有代碼才能開始測試)
3. 孤立政策【推薦使用】方式:選擇需要進行測試的函數進行測試,優點:選擇重要的代碼進行測試,測試構成中免不了測的某些函數會調用别的函數,是以一定要學會打樁
注意:打樁-->打樁就是模拟編寫一個我們需要引用的函數,模拟定義被調用的函數名,提示:一般我們隻模拟寫個函數名,直接傳回相應的結果即可(return 結果;pass)
示例:def fun_1(self):
return true
四、單元測試政策-實作
測試政策實作:把我們標明的代碼,而且保證標明的代碼能獨立運作(已經打完樁),轉向流程圖、流圖及用例的過程
測試政策實作如何操作:1. 将測試代碼轉換成流程圖;2. 根據流程圖轉換為流圖,有了流圖測試用例就出來了

流程圖
什麼是流圖?
概念:表達程式業務邏輯的複雜度一種示意圖
構成:
1) 圈:判斷條件(一個條件一個圈)、語句塊(一條或多條語句,挨着的語句可以用一個圈表示)兩者都圈
2) 線:箭頭指向的線,用來連接配接圈和圈的指向
流圖的目的:
1) 确定單元的複雜度級别
2) 确定測試用例
備注: 路徑的個數為複雜度的級别(一條路徑為1個複雜度級别),有N個條件最少有N條路徑,最多N+1條路徑

流圖
* 路徑:2 (1-2-3,1-3);複雜度:2;用例個數:2

根據流圖寫的測試用例
五、單元測試——執行
通過單元測試架構對要進行測試代碼的實踐過程需求:通過Python語言編寫一個運算的類(Calc),類中包含兩個函數:add(self,a,b) 傳回a+b之和;sub(self,a,c) 傳回a-c之差
- 導包 UnitTest 、Calc類
- 建立單元測試類 Test01(內建 unitTest.TestCase)
- 建立testAdd()函數
- 導入Calc類中的add函數
- 添加斷言
- 建立testSub()函數
- 導入Calc類中的sub函數
- 添加斷言
- 執行測試:unittest.main()import unittest
# 導入要測試的 Calc類
from UT.Day02.Code.test01_lx01import Calc
class Test_Calc(unittest.TestCase):
def setUp(self):
print("setUp被執行")
def tearDown(self):
print("tearDown被執行")
def test_add(self):
result=Calc().add(10,10)
print("test_add方法被執行")
self.assertEqual(result,20)
def test_sub(self):
result=Calc().sub(10,20)
print("test_sub方法被執行")
self.assertEqual(result,-10)
if __name__== '__main__':
# main運作目前類中所有test開頭的方法
unittest.main()
資料直接寫入代碼中,如果數量龐大,我們需要頻繁改動資料或複制備援代碼進行實作資料測試目的。做不到資料分離(代碼和資料分開),不便維護
參數化
根據需求動态擷取資料并進行指派的過程
參數化方式:XML格式(1); CSV格式(2);JSON串 (3);TXT文本(4)
提示:括号内為推薦使用優先級,從小到大;1-2為擴充了解,3-4建議重點了解
XML格式:
XML是一種标記語句,很類似HTML标記語言;字尾 .xml;XML是傳輸和存儲資料,焦點在資料;HTML是顯示資料,焦點在外觀;XML不适合大量參數化資料時使用XML格式:
單元測試
XST
2008
39.95
1. 必須有XML聲明語句:<?xml version="1.0" encoding="UTF-8"?>
2. 必須要有一個根元素,如:
3. 标簽大小寫敏感
4. 屬性值用雙引号
5. 标簽成對
6. 元素正确嵌套
7. 标簽名可随意命名,但有以下限制(注意:命名允許重複)
1) 不能以數字或者标點符号開始參
2)不能以字元 “xml”(或者 XML、Xml)開始
3) 名稱不能包含空格需求:對三角形案例單元測試使用XML格式進行參數化
被測代碼段:
class Sjx(): def sjx(self,a,b,c):
# 判斷是否為三角形
if a+b>c and a+c>b and b+c>a:
# 判斷是否為等邊三角形
if a==b and b==c:
return "等邊三角形"
elif a==b or a==c or b==c:
return "等腰三角形"
else:
return "普通三角形"
else:
return "不是三角形"
if __name__ == '__main__':
print(Sjx().sjx(2,3,4))
--------------------------------------------------------------------------------------------------------------------------
操作步驟:1. 編寫XML資料檔案;2. 編寫讀取XML子產品函數;3. 單元測試子產品中引用XML讀取函數;4. 執行
編寫XML資料檔案:
2
3
4
普通三角形
2
2
2
等邊三角形
2
2
3
等腰三角形
2
3
2
等腰三角形
3
2
2
等腰三角形
3
2
1
不是三角形
1
2
3
不是三角形
2
3
1
不是三角形
編寫讀取XML子產品函數# 導包
minidomfrom xml.dom import minidom
class Read_Xml():
def readXml(self,node,num,nodeChild):
# 解析xml文檔
dom=minidom.parse("../DataPool/sjx.xml")
# 擷取文檔對象
root=dom.documentElement
# 擷取bian元素
element=root.getElementsByTagName(node)[int(num)]
# 擷取指定bian元素 如b1
return element.getElementsByTagName(nodeChild)[0].firstChild.data
def get_len(self,node):
# 解析xml文檔
dom=minidom.parse("../DataPool/sjx.xml")
# 擷取文檔對象
root=dom.documentElement
# 擷取bian元素
return len(root.getElementsByTagName(node))
if __name__ == '__main__':
print(Read_Xml().readXml("bian",0,"b3"))
print(Read_Xml().get_len("bian"))
單元測試子產品中引用XML讀取函數;執行# 導包 unittest、三角形函數、讀取xml資料類
import unittest
from UT.Day02.Code.sjx import Sjx
from UT.Day02.ReadData.read_xml import Read_Xml
# 執行個體化三角形
sjxClass=Sjx()
# 執行個體化讀取xml類
readXmlClass=Read_Xml()
class Test_Xml(unittest.TestCase):
def test001(self):
for i in range(readXmlClass.get_len("bian")):
# 目的測試三角形程式
result=sjxClass.sjx(int(readXmlClass.readXml("bian",i,"b1")), int(readXmlClass.readXml("bian", i, "b2")), int(readXmlClass.readXml("bian", i, "b3")))
# 添加斷言,判斷三角形程式傳回的結果是否符合我們預期結果 self.assertEqual(result,readXmlClass.readXml("bian", i, "expect")) print(readXmlClass.readXml("bian",i,"b1"), readXmlClass.readXml("bian", i, "b2"), readXmlClass.readXml("bian", i, "b3"), readXmlClass.readXml("bian", i, "expect"),"--》驗證通過")
重點分析:
1. 導入XML包 from xml.dom import minidom
2. 加載解析 dom=minidom.parse(filename)
3. 擷取對象 root=dom.documentElement
4. 擷取子元素 aas=root.getElementsByTagName(one)[0]
5. 擷取子元素值 aas.getElementsByTagName(two)[0].firstChild.data
CSV格式:
CSV是一種以逗号做分割的表格格式; 字尾 .csv使用CSV實作三角形案例參數化-操作步驟
1. 建立CSV檔案
2. 編寫CSV讀取子產品函數
3. 單元測試-引用CSV讀取函數
4. 執行
建立CSV檔案:
2,2,2,等邊三角形2,2,3,等腰三角形2,3,2,等腰三角形3,2,2,等腰三角形1,2,3,不是三角形2,3,1,不是三角形3,2,1,不是三角形2,3,4,普通三角形
編寫CSV讀取子產品函數:
# 導包
import csv
class Read_Csv():
def readCsv(self):
# 打開csv
with open("../DataPool/sjx.csv","r",encoding='utf-8') as f:
datas=csv.reader(f)
# 建立空清單,把單行傳回的清單添加成整體的清單
lines=[]
for data in datas:
# 添加數組
lines.append(data)
return lines
if __name__ == '__main__':
print(Read_Csv().readCsv())
單元測試-引用CSV讀取函數;執行:
# 導入unittest、三角形函數、csv讀取參數類
import unittest
from UT.Day02.Code.sjx import Sjx
from UT.Day02.ReadData.read_csv import Read_Csv
# 執行個體化三角形
sjxClass=Sjx()
# 執行個體化讀取csv工具
readCsvClass=Read_Csv()
class Test_Csv(unittest.TestCase):
# 測試三角形函數程式
def test001(self):
for i in range(len(readCsvClass.readCsv())): result=sjxClass.sjx(int(readCsvClass.readCsv()[i][0]),
int(readCsvClass.readCsv()[i][1]),
int(readCsvClass.readCsv()[i][2]))
# 斷言:三角新運作完後傳回的結果和預期結果做對比 self.assertEqual(result,readCsvClass.readCsv()[i][3])
print(readCsvClass.readCsv()[i][0], readCsvClass.readCsv()[i][1], readCsvClass.readCsv()[i][2], readCsvClass.readCsv()[i][3],"---》驗證通過")
if __name__ == '__main__':
unittest.main()
重點分析:
1. 導包 import csv
2. 打開csv檔案:
with open("../Data/sjx.csv","r",encoding="utf-8") as f:
lines=csv.reader(f)
JSON串:
一種輕量級資料交換格式;字尾名 .json ;提示: 接口測試一般使用JSON為接口傳遞資料規範格式
格式:{"name":"張三","age":28};提示:由鍵值對組成,健名和值之間使用分号(:)分割,多個鍵值對之間使用逗号(,)分割使用JSON實作三角形案例參數化-操作步驟
1. 編寫JSON檔案
2. 編寫JSON讀取子產品函數
3. 單元測試-引用JSON讀取函數
4. 執行
編寫JSON檔案 :
{"data": [ {"b1":2,"b2":2,"b3":2,"expect":"等邊三角形"}, {"b1":2,"b2":2,"b3":3,"expect":"等腰三角形"}, {"b1":2,"b2":3,"b3":2,"expect":"等腰三角形"} ]}
編寫JSON讀取子產品函數 :# 導包 json
import json
# 打開檔案流
class Read_Json():
def readJson(self):
with open("../DataPool/sjx.json","r",encoding="utf-8") as f:
# 調用load()
datas=json.load(f)
# 傳回字典data鍵名對應的值
return datas["data"]
if __name__ == '__main__':
print(Read_Json().readJson())
單元測試-引用JSON讀取函數;執行 :# 導包 unittest 、三角形函數程式、讀取json類
import unittest
from UT.Day02.Code.sjx import Sjx
from UT.Day02.ReadData.read_json import Read_Json
# 執行個體化三角形
sjxClass=Sjx()
# 執行個體化讀取JSON類
readJsonClass=Read_Json()
class Test_Json(unittest.TestCase):
# 測試三角形
def test001(self):
for i in range(len(readJsonClass.readJson())):
# 調用三角形函數
result=sjxClass.sjx(int(readJsonClass.readJson()[i]["b1"]), int(readJsonClass.readJson()[i]["b2"]), int(readJsonClass.readJson()[i]["b3"]))
# 斷言 三角形傳回的結果是否與預期結果相符 self.assertEqual(result,readJsonClass.readJson()[i]["expect"])
# 列印運作參數及結果
print(readJsonClass.readJson()[i]["b1"], readJsonClass.readJson()[i]["b2"], readJsonClass.readJson()[i]["b3"], readJsonClass.readJson()[i]["expect"],"---> 通過!")
if __name__ == '__main__':
unittest.main()
難點分析
1. 導入JSON包(import JSON)
2. 打開JSON檔案并解析
with open('../DataXML/sjx.json','r',encoding='utf-8') as f:
file=json.load(f)
TXT文本:
一種純文字格式; 字尾名 .txt;TXT文本優點: 編寫測試資料友善;使用子產品函數讀取時便捷使用TXT實作三角形案例參數化-操作步驟
1. 建立txt文本并寫入測試資料
2. 編寫讀取txt子產品函數
3. 單元測試-引用JSON讀取函數
4. 執行
建立txt文本并寫入測試資料:第一條邊,第二條邊,第三條邊,預期結果
2,2,2,等邊三角形
2,2,3,等腰三角形
2,3,2,等腰三角形
3,2,2,等腰三角形
1,2,3,不是三角形
2,3,1,不是三角形
3,2,1,不是三角形
2,3,4,普通三角形
編寫讀取txt子產品函數 :class Read_Txt():
def readTxt(self):
# 打開txt檔案流
with open("../DataPool/sjx.txt","r",encoding="utf-8") as f:
# 通過檔案流調用讀取的方法讀取資料--->所有行
datas=f.readlines()
# 建立清單 --》添加分割後的單行清單資料
lines=[]
# 周遊
for data in datas:
'''
strip():去除字元串前後回車,空格,tab鍵
split():使用指定符号進行分割字元串并以清單格式傳回分割後的資料
'''
# 把分割後的單行清單資料添加到整體的列中,并傳回
lines.append(data.strip().split(","))
# 傳回資料 [1:] 傳回從下标1開始傳回資料
return lines[1:]
'''
f.read() #讀取所有資料
f.readline() # 讀取單行
f.readlines() # 讀取所有行
'''
if __name__ == '__main__':
print(Read_Txt().readTxt())
單元測試-引用JSON讀取函數;執行 :# 導包 unittest、三角形、txt讀取
import unittest
from UT.Day02.Code.sjx import Sjx
from UT.Day02.ReadData.read_txt import Read_Txt
# 執行個體化三角形
sjxClass=Sjx()
# 執行個體化txt
readTxtClass=Read_Txt()
class Test_Txt(unittest.TestCase):
# 測試三角形程式
def test001(self):
for i in range(len(readTxtClass.readTxt())):
# 調用三角形方法
result=sjxClass.sjx(int(readTxtClass.readTxt()[i][0]),
int(readTxtClass.readTxt()[i][1]),
int(readTxtClass.readTxt()[i][2]))
# 調用斷言,判斷三角形程式執行後的結果是否與預期結果相符
self.assertEqual(result,readTxtClass.readTxt()[i][3])
# 為了檢視友善,我把執行成功後的資料列印一下
print(readTxtClass.readTxt()[i][0], readTxtClass.readTxt()[i][1], readTxtClass.readTxt()[i][2], readTxtClass.readTxt()[i][3],"-->通過!")
if __name__ == '__main__':
unittest.main()
難點分析:導包:不需要讀取方法:readlines()
1. 如何讀取txt文本?
with open(r'../DataXML/三角形.txt','r',encoding='utf-8') as f:
2. 如何去除行尾/n換行符?
line.strip()

單元測試總結
--------------------------------------------------------------------------------
HTML報告生成
如何生成HTML報告?導入HTML報告模闆模闆:HTMLTestRunner.py編寫生成HTML子產品
# 導入unittest包
import unittest
# 導入 HTMLTestRunner模闆包
from UnitTest.Day02.ReadData.HTMLTestRunner import HTMLTestRunner
#導入時間包
import time
# 定義測試子產品路徑
dirpath='.'
discorver=unittest.defaultTestLoader.discover(dirpath,pattern='test*.py')
if __name__=='__main__':
#存放報告的檔案夾
report_dir='../TestReport'
#報告名稱含時間,時間格式
now=time.strftime("%Y-%m-%d %H_%M_%S")
#報告完整路徑+名稱
report_name=report_dir+'/'+now+'result.html'
#打開報告寫入結果
with open(report_name,'wb')as f:
runner=HTMLTestRunner(stream=f,title="UnitTest Report-SJX",description='Test Case Report')
runner.run(disconver)