測試理論回顧:
黑盒測試:是針對整個產品系統進行的測試,驗證系統是否滿足需求規格的定義,及軟件產品的正確性和性能等是否滿足其需求規格的要求。
灰盒測試:是介于白盒測試與黑盒測試之間的一種測試,灰盒測試多用于集成測試階段,不僅關注輸出、輸入的正確性,同時也關注程序內部的情況。
白盒測試:是通過程序的源代碼進行測試而不使用用戶界面。這種類型的測試需要從代碼內部在算法,路徑,條件等等中的缺點或者錯誤,進而加以修正。
單元測試:
單元測試是針對程序的最小單元來進行正確性檢驗的過程。單元:一個單元可能是單個程序、類、對象、方法(函數)等。?優點是可以減少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格式:
<?xml version="1.0" encoding="UTF-8"?>
<book category="面授">
????<title>單元測試</title>
????<author>XST</author>
????<year>2008</year>
????<price>39.95</price>
</book>1. 必須有XML聲明語句:<?xml version="1.0" encoding="UTF-8"?>
2. 必須要有一個根元素,如:<book>
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數據文件:
<?xml version="1.0" encoding="UTF-8" ?>
<SJX>
?????<bian>
?????????<b1>2</b1>
?????????<b2>3</b2>
?????????<b3>4</b3>
?????????<expect>普通三角形</expect>
?????</bian>
?????<bian>
?????????<b1>2</b1>
?????????<b2>2</b2>
?????????<b3>2</b3>
?????????<expect>等邊三角形</expect>
?????</bian>
?????<bian>
?????????<b1>2</b1>
?????????<b2>2</b2>
?????????<b3>3</b3>
?????????<expect>等腰三角形</expect>
?????</bian>
?????<bian>
?????????<b1>2</b1>
?????????<b2>3</b2>
?????????<b3>2</b3>
?????????<expect>等腰三角形</expect>
?????</bian>
?????<bian>
?????????<b1>3</b1>
?????????<b2>2</b2>
?????????<b3>2</b3>
?????????<expect>等腰三角形</expect>
?????</bian>
?????<bian>
?????????<b1>3</b1>
?????????<b2>2</b2>
?????????<b3>1</b3>
?????????<expect>不是三角形</expect>
?????</bian>
?????<bian>
?????????<b1>1</b1>
?????????<b2>2</b2>
?????????<b3>3</b3>
?????????<expect>不是三角形</expect>
?????</bian>
?????<bian>
?????????<b1>2</b1>
?????????<b2>3</b2>
?????????<b3>1</b3>
?????????<expect>不是三角形</expect>
?????</bian>
</SJX>編寫讀取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)