這是一套介紹接口自動化架構的系列文章 ,其目的就是讓零基礎學習自動化測試的人員通過該系列文章能将自動化搭建起來 ,如果這些文章對你有用,建議點贊收藏。本篇為自動化架構的第二篇 ,如若沒有看第一篇的話,建議先看下上一篇文章 :測試新手如何搭建自動化架構 ?手把手教會從0到1的搭建過程。
在編寫自動化架構過程中 ,我們首先想到的就是選擇一個合适的測試架構 ,目前常用的測試架構有unittest和pytest , unittest比較簡單,适合入門者學習 ;而pytest比較強大,适合後期進階 。本文主要介紹的就是unittest架構 。接下來 ,我們從以下三個問題開始說明:
- unittest是什麼 ?
- unittest的作用是什麼 ?它有哪些功能 ?
- unittest如何使用 ?
1.unittest是什麼 ?
unittest是python自帶的一個測試架構,是以你無需下載下傳,直接導包使用,具體如下 :
import unittest
當然作為測試人員,主要用它來做自動化測試,比如接口自動化、web自動化或app自動化測試 。
在這裡我們需要先搞清楚一個問題 ,無論做什麼樣的自動化 ,其目的就是為了做回歸測試 。在平時工作中,我們一般根據階段的不同,往往選取的回歸測試用例也不同,比如在一輪測試結束後往往會把出現bug的用例再回歸一遍,在上線之前也會選取本次疊代的功能作為回歸測試用例 。 而這些不同的選擇其實就是選取的政策。而選取的用例集合往往會叫它測試套件 ,說白了測試套件就是用例的組合而已 。比如下圖
是以,要想做回歸測試 ,必須滿足如下條件 :
- 能進行測試斷言 ,即測試功能的預期值和實際值的比對 。
- 能将不同的測試用例添加到不同的套件中
- 能選擇不同的測試套件進行運作
- 能統計最終測試用例運作結果 。
2.unittest有哪些功能 ?
針對如上的條件,unittest包正好也實作了以上的功能 ,具體如下 :
可以看到 ,上面的5個類主要實作了四個功能 ,分别是測試用例斷言(TestCase) , 組裝測試套件(TestSuite和TestLoader) ,運作測試套件(TextTestRunner)以及測試結果彙總TestResult . 當然每個類中又都包含了若幹方法 ,下表列舉出常用的方法 ,掌握如下測試方法 ,編寫自動化測試架構就沒有問題 。
3.unittest如何使用 ?
要想使用unittest ,需要有一個測試對象 ,我們咱先編寫一段登入的代碼 ,然後使用unittest對這個登入功能進行測試 。
# 實作登入函數
def login(username,password):
# 使用者名為空或密碼為空 ,給出提示
if username is None or username == '':
return {'code':1,'message':'使用者名不能為空!'}
if password is None or password == '':
return {'code':2,'message':'密碼不能為空!'}
# 使用者名和密碼比對登入成功
if 'root' == username and '123456' == password:
return {'code':0,'message':'登入成功!'}
# 使用者名和密碼不比對的情況
return {'code':3,'message':'使用者名或密碼錯誤!'}
如果你對以上登入進行測試的話 ,會編寫那些測試用例呢 ? 你可能會列舉出以下幾條 :
- case1 : 輸入正确的使用者名和正确的密碼進行登入
- case2 : 輸入正确的使用者名和錯誤的密碼進行登入
- case3 : 輸入正确的使用者名和空的密碼進行登入
3.1 TestCase使用
接下來讓我們用unittest來進行測試 ,而通過上面可以看到,進行用例的測試就需要用到TestCase類。當然,你要想要使用這個類,還必須遵循一些unittest的原則 :
unittest的編寫原則:
1. 編寫的測試用例必須是一個類 ,而且此類必須要繼承TestCase 。繼承寫法 :A(B)
2. 編寫測試用例類必須是以大寫的Test開頭
3. 編寫的測試用例方法是以小寫test開頭
4. 測試子產品也最好是以小寫的test開頭
5. 每一條測試用例對應一個方法(建議)
可以參考如下圖
接下來使用TestCase中的斷言方法進行斷言 :
1.測試用例斷言方法 :
斷言方法 | 說明 | 使用頻度 |
assertEqual(a,b) | 斷言a ==b | 極高 |
assertNotEqual(a,b) | 斷言a!=b | 極少 |
assertTrue(x) | x is True | 一般 |
assertFalse(x) | 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 包含 | 較高 |
assertGreater(a,b) | a>b | 較高 |
assertGreaterEqual(a,b) | a>=b | 較高 |
assertLess(a,b) | a<b | 較高 |
assertLessEqual(a,b) | a<=b | 較高 |
是以,以上代碼加入斷言(測試)後就是下面這樣的情況,這裡就主要用到了assertEqual方法進行斷言。
import unittest
from pack01_unittest.login import login
class TestLogin(unittest.TestCase):
# case1 : 輸入正确的使用者名和正确的密碼進行登入
def test_login_success(self):
# 實際結果 :程式的輸出
login_result = login('root','123456')
self.assertEqual(0,login_result.get('code'))
self.assertEqual('登入成功!',login_result.get('message'))
# case2 : 輸入正确的使用者名和錯誤的密碼進行登入
def test_password_is_wrong(self):
login_result = login('root', '1234567')
self.assertEqual(3, login_result.get('code'))
self.assertEqual('使用者名或密碼錯誤!', login_result.get('message'))
# case3 : 輸入正确的使用者名和空的密碼進行登入
def test_password_is_null(self):
login_result = login('root', '')
self.assertEqual(2, login_result.get('code'))
self.assertEqual('密碼不能為空!', login_result.get('message'))
2.初始化和清除方法
方法 | 說明 | 執行順序 |
setUp() | 初始化方法,在此方法編寫初始化代碼,主要是為測試用例準備初始狀态,比如建立對象 | 在每個測試方法前運作一次 |
tearDown() | 清空方法,在此方法内編寫清空操作的一些代碼,比如運作用例後,一些資料記錄已在資料庫發生變化,在這裡将其還原到未執行前的狀态。 | 在每個測試方法結束後執行一次 |
setUpClass() | 和setUp作用一樣,是類方法,在其上面必須加@classmethod | 在運作測試用例的最開始運作一次,隻運作一次 |
tearDownClass() | 和tearDown()作用一樣,,是類方法,在其上面必須加@classmethod | 在運作測試用例的結束運作一次,隻運作一次 |
3.2 TestSuite使用
在上面我們已經提過 ,TestSuite類其實就是将不同測試用例添加到一個套件中 ,僅僅隻是添加,若想運作該套件,還必須借助于另外一個類,這個類我們在下面會介紹 ,具體見圖
是以,此類下主要的兩個方法都是用例做測試用例添加操作的。
1.主要使用方法
方法 | 說明 | 備注 |
addTest(測試用例) | 将測試用例添加到一個套件中 | 測試用例的添加格式:類名('方法名'),方法名後不能加括号 |
addTests(測試用例清單) | 将測試用例的清單添加到一個套件中 | 清單添加格式 :[測試用例1,測試用例2,測試用例n] |
2.具體使用步驟
- 建立測試套件對象
- 添加測試用例到套件中
- 運作測試套件,需要借助于TextTestRunner類。
# 1. 建立一個測試套件
suite = unittest.TestSuite()
# 2. 将測試用例加入到套件中
suite.addTest(TestLogin('test_password_is_wrong')) # 将測試用例test_password_is_wrong添加到suite套件中
suite.addTest(TestLogin('test_password_is_null')) # 将測試用例test_password_is_null添加到套件中。
print(suite)
輸出後的suite就是這樣 ,它是一個清單對象:
<unittest.suite.TestSuite tests=[<main.TestLogin testMethod=test_password_is_wrong>, <main.TestLogin testMethod=test_password_is_null>]
因為目前還沒有介紹到TextTestRunner此類 ,是以暫時還沒法運作該套件的用例 。
3.3 TestLoader使用
和TestSuite類的作用一樣,也是将不同測試用例添加到一個套件中 ,僅僅隻是添加,若想運作該套件,還必須借助于另外一個類。
但是和TestSuite不同的是,TestSuite是一條一條的添加,每次添加一條用例就需要調用一次addTest方法,是以,當需要添加的用例很多時,添加起來就會很麻煩。
而TestLoader添加方式通過比對進行批量添加 ,它是一批一批的添加 ,用例很多時,這種方式添加起來就會友善 。
1.主要使用方法:discover(test_dir, pattern=比對模式)
參數說明:
- test_dir: 為指定的測試用例的目錄,它會搜尋該目錄的所有檔案
- pattern:為比對模式,如要查找所有的.py格式檔案,你就可以寫為'test*.py',這樣就會搜尋test_dir目錄下所有以test開頭,以py結尾的檔案。
2.具體使用步驟
- 建立測試套件對象
- 添加測試用例到套件中,比對某一特征的用例添加 ,
- 運作測試套件,需要借助于TextTestRunner類。
# 1. 建立套件
suite = unittest.TestLoader()
# 2. 通過特征比對出相關用例,比如隻比對test_login的用例
suite = suite.discover('.','test_login*.py')
3.4 TextTestRunner使用
此類的主要作用就是運作測試套件 ,是以它裡面隻有一個測試方法run() ,通過它就可以運作測試套件中的用例 ,而上面的TestSuite類和TestLoader類生成的套件就的需要這個方法去運作的 。
1.主要使用方法:run(suite),需要說明的是 ,suite就是通過TestSuite或者TestLoader建立的測試套件對象 。
運作測試套件也比較簡單,具體的代碼如下 :
# 1.通過TextTestRunner類生成runner對象
runner = unittest.TextTestRunner()
# 2.調用run方法運作測試套件
runner.run(suite)
2.運作套件流程,
知道了上面的方法 ,你就可以按照如下的流程進行運作測試套件了 。
- 建立測試套件對象
- 通過逐一添加或比對添加的方式将用例添加到套件中
- 運作測試套件。
以下為通過TestSuite方式添加:
# 1. 建立一個測試套件
suite = unittest.TestSuite()
# 2. 将測試用例加入到套件中
suite.addTest(TestLogin('test_password_is_wrong')) # 将測試用例test_password_is_wrong添加到suite套件中
suite.addTest(TestLogin('test_password_is_null')) # 将測試用例test_password_is_null添加到套件中。
# 3.通過run方法運作該測試套件
runner = unittest.TextTestRunner()
runner.run(suite)
以下為TestLoader方式添加:
# 1. 建立一個測試套件
suite = unittest.TestSuite()
# 2. 将測試用例加入到套件中
suite = suite.discover('.','test_login*.py')
# 3.通過run方法運作該測試套件
runner = unittest.TextTestRunner()
runner.run(suite)
3.5 TextTestResult
首先這個類主要是供TextTestRunner它使用的 ,通過TextTestRunner可以将測試結果簡單的輸出到文本中 ,顯示結果也比較簡單 ,是以我們一般也不會用它做測試報告,而使用其它的插件來生成測試報告,是以在測試過程中,無需關注這個類 。
4.總結
5.項目實踐
如果你看過第一篇文章,我們曾經搭建了一套接口自動化架構 ,如果你沒看過,請移步到第一篇文章,傳送門 :測試新手如何搭建自動化架構 ?手把手教會從0到1的搭建過程。
建議你按照那個架構先搭建起來 ,接下來我們就可以在那個架構裡面編寫測試用例了 ,而編寫用例所進行的斷言就是使用的是unitest.
雖然,我們目前還不知道該如何請求接口,但是沒關系 ,我們可以先假設已經将結果請求回來了 。是以 ,你可以在測試用例中添加一個測試子產品,專門用于測試登入接口,
以下是test_login.py中的代碼 ,需要說明的是,因為現在還沒有介紹如何請求接口和查詢資料庫,故這裡隻能暫時将傳回的結果寫死,以友善說明,重點在說明傳回的結果如何進行斷言 。
import unittest
# 模拟接口傳回的結果
login_result = {
"status": 1,
"msg": "登陸成功",
"result": {
"user_id": 2638,
"email": "",
"password": "519475228fe35ad067744465c42a19b2",
"paypwd": None,
"sex": 0,
"birthday": 0,
"user_money": "2939.00",
"frozen_money": "0.00",
"distribut_money": "0.00",
"underling_number": 0,
"pay_points": 150,
"address_id": 0,
"reg_time": 1681047891,
"last_login": 1681047891,
"last_ip": "",
"qq": "",
"mobile": "15388888888",
"mobile_validated": 1,
"oauth": "",
"openid": None,
"unionid": None,
"head_pic": None,
"province": 0,
"city": 0,
"district": 0,
"email_validated": 0,
"nickname": "15388888888",
"level": 2,
"discount": "0.98",
"total_amount": "10908.00",
"is_lock": 0,
"is_distribut": 1,
"first_leader": 2636,
"second_leader": 0,
"third_leader": 0,
"token": "10af0e236dedbc8ae00273b00be25ee3",
"message_mask": 63,
"push_id": "0",
"distribut_level": 0,
"level_name": "銅牌會員"
},
"url": ""
}
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
# 這裡的資料是模拟從資料庫查詢出來的結果
self.user_money = "2939.00"
self.mobile = "15388888888"
self.pay_points = 150
def tearDown(self) -> None:
# 這裡模拟關閉資料庫
pass
# case1 : 測試登入
def test_login(self):
# 實際結果 :這裡的login_result是模拟接口傳回的結果 ,暫時先将此結果寫死直接拿過來用。
self.assertEqual(1, login_result.get('status'))
self.assertEqual('登陸成功', login_result.get('msg'))
self.assertEqual(self.user_money, login_result.get('result').get('user_money')) #斷言使用者餘額
self.assertEqual(self.mobile,login_result.get('result').get('mobile')) # 斷言使用者手機号
self.assertEqual(self.pay_points,login_result.get('result').get('pay_points')) #斷言使用者積分