一篇文章带你了解Python基础测试工具——UnitTest
一篇文章帶你了解Python基礎測試工具——UnitTest
測試人員一般使用Python作為主語言腳本來進行自動化開發(fā),而Python自帶的UnitTest腳本通常就是測試人員首先掌握的
那么本篇文章我們將來介紹Python的最基本自動化工具UnitTest來開始我們自動化的第一步
我們這篇文章將從以下角度進行講解:
-
UnitTest基本介紹
-
UnitTest組成部分
-
UnitTest進階部分
UnitTest基本介紹
首先我們將會對自動化和UnitTest進行一個基本的介紹
自動化腳本介紹
首先我們需要知道什么叫做自動化腳本:
- 采用代碼的形式書寫項目,采用項目運行的方式來代替人工測試
自動化腳本的作用主要有以下幾點:
- 解放人力資源,杜絕人為性錯誤,加快測試速度
- 使沒有代碼基本的人可以借助工具快速上手進行自動化測試
UnitTest基本介紹
下面我們來簡單介紹一下UnitTest:
- UnitTest是Python自帶的一款單元測試框架,無需安裝即寫即用
我們來針對上面幾個詞匯進行講解:
# 自帶框架(官方框架)和第三方框架
# 自帶框架:跟隨Python官方一共上架的框架,只需要下載Python就可以直接使用,例如Python的UnitTest,OS,requests等
# 第三方框架:由非官方開發(fā)人員所開發(fā)的可以在Python上導入并使用的框架,例如我們后來會學到的pytest,httpRunner等
# 單元測試
# 單元測試一般是只單單針對后端功能進行測試,而自測環(huán)節(jié)通常是由后端進行的
# 但是我們測試人員也可以在后端開發(fā)期間或前后端聯(lián)調階段去進行單元測試以確定后端功能來減少提測時的錯誤率
最后我們簡單介紹一下UnitTest的優(yōu)點:
- 能夠組織多個?例去執(zhí)?
- 提供豐富的斷??法
- 能夠?成測試報告
UnitTest組成部分
下面我們來正式學習UnitTest的組成部分,來真正學習這個框架
UnitTest組成概述
我們首先在這里簡單介紹一下UnitTest的整體組成部分:
# TestCase
# 簡稱:測試用例(核心模塊)
# TestCase是UnitTest里面的一個類名,我們需要創(chuàng)建一個類去繼承該TestCase
# 然后我們需要在該類型中書寫測試方法def,后續(xù)我們就可以直接去執(zhí)行該TestCase中的Case或采用其他方法集中執(zhí)行
# TestSuite
# 簡稱:測試套件
# 正常來說我們的TestCase只能在當前類中去進行執(zhí)行,如果我們的Case劃分寫在不同TestCase下,那么我們就不能一同執(zhí)行
# 但是我們可以采用TestSuite將某個TestCase中的Case加入到該TestSuite中,然后采用執(zhí)行器一同執(zhí)行
# TestLoader
# 簡稱:測試加載器
# TestLoader其實和TestSuite是一樣的作用
# 但不同的是TestSuite一次只能添加一個TestCase中的一個Case或一個Testcase的所有Case
# 但是TestLoader可以根據匹配定理來一次性添加多個同一文件夾下的Testcase來實現快速添加的功能
# TestRunner
# 簡稱:測試執(zhí)行器
# TestRunner的主要功能就是為了執(zhí)行TestSuite和TestLoader從而獲得對應的測試數據
# Fixture
# 簡稱:測試夾具
# Fixture的主要功能就是在測試方法執(zhí)行的前后去添加一些前置條件,比如打開瀏覽器,打開某個網頁等
# Fixture主要被劃分為三個級別:文件級別,類級別,方法級別,我們都在下面一一講述的
TestCase
首先我們來學習UnitTest的核心點TestCase:
# 首先我們來概述TestCase
# TestCase就是一個類,一個需要被我們所繼承的類
# TestCase的使用我們下面將采用一個用例來講解
# 首先我們如果想要TestCase,我們需要導入UnitTest
import unittest
# 然后我們需要自定義一個類,這個類需要繼承Testcase
class TestDemo(unittest.TestCase):
# 下面我們在該類中所書寫的每個方法都會變成一個case,在執(zhí)行過程中被我們所執(zhí)行
# 此外我們還需要注意:我們的每個方法都必須以test_開頭,否則系統(tǒng)不會將他判斷為case
def test_method1(self):
print('測試?法 1')
def test_method1(self):
print('測試?法 2')
# 正常來說我們的TestCase是可以單獨去執(zhí)行的
# 如果我們想執(zhí)行該TestCase下的所有方法,我們可以將光標對準Class行,右鍵run執(zhí)行,從而獲得結果
# 如果我們想執(zhí)行該TestCase下的某個方法,我們可以將光標對準該def行,右鍵run執(zhí)行,從而獲得結果
TestRunner&TestSuite&TestLoader
下面我們來一起介紹這三個組件,因為他們之間有非常緊密的聯(lián)系:
# 首先我們先來介紹TestRunner
# 該類就是一個單純的執(zhí)行機器,我們只需要創(chuàng)建對應的實體,然后調用該實體的run方法來執(zhí)行對應的Case組件即可
# 我們先來介紹TestSuite
# 首先我們需要導入unittest
import unittest
# 我們需要注意,如果我們的TestSuite引用到了其他TestCase類的case方法,那么我們需要手動import導入對應類
# 當然這里推薦alt+enter自動導入
from testcase1 import TestDemo1
from testcase2 import TestDemo2
# 然后下面的操作我們可以單獨列在一個main方法中,也可以直接書寫
if __name__ == '__main__':
# 首先我們創(chuàng)建一個TestSuite
suite = unittest.TestSuite()
# 然后我們需要采用一種格式來添加case到該suite中:suite.addTest(類名('方法名'))
suite.addTest(TestDemo1('test_method1'))
suite.addTest(TestDemo1('test_method2'))
suite.addTest(TestDemo2('test_method1'))
suite.addTest(TestDemo2('test_method2'))
# 當然我們的suite也是存在一種快速導入一個TestCase的所有Case的方法:suite.addTest(unittest.makeSuite(類名))
# 但是我們需要注意makeSuite方法是沒有提示信息的,因為該方法其實是unittest之前版本就想要刪除的方法,但是推薦使用
suite.addTest(unittest.makeSuite(TestDemo1))
# 最后我們創(chuàng)建一個TestRunner去執(zhí)行
runner = unittest.TestRunner()
runner.run(suite)
# 下面我們來介紹TestLoader
# 首先我們需要導入unittest
import unittest
# 然后下面的操作我們可以單獨列在一個main方法中,也可以直接書寫
if __name__ == '__main__':
# 同理我們需要創(chuàng)建一個TestLoader對象,當然我們也可以直接在創(chuàng)建過程中使用方法然后直接返回TestLoader對象
# TestLoader有這么一個方法:unittest.TestLoader().discover('用例所在的路徑', '用例的代碼文件名')
# 該用例的代碼文件名是可以支持*作為任意字符數,也可以使用?來代替單個字符數
suite = unittest.TestLoader().discover('./case', '*case*.py')
# 同理我們需要一個TestRunner來執(zhí)行run方法,當然也可以化作一步來執(zhí)行
unittest.TestRunner().run(suite)
Fixture
最后我們來介紹一下Fixture測試夾具:
# 首先我們需要知道Fixture測試夾具的主要用途就是做一些前置條件或者結束條件,它會在某些特定條件下執(zhí)行
# 首先是方法級別的Fixture測試夾具
# 它是在每個測試方法(用例代碼) 執(zhí)行前后都會自動調用的結構
# 方法執(zhí)行之前
def setUp(self):
每個測試方法執(zhí)行之前都會執(zhí)行
pass
# 方法執(zhí)行之后
def tearDown(self):
每個測試方法執(zhí)行之后都會執(zhí)行
pass
# 然后是針對類級別的Fixture測試夾具
# 它是在每個測試類中所有方法執(zhí)行前后 都會自動調用的結構(在整個類中執(zhí)行之前或之后執(zhí)行一次)
# 需要注意:類級別的Fixture 方法, 是一個 類方法
# 類中所有方法之前
@classmethod
def setUpClass(cls):
pass
# 類中所有方法之后
@classmethod
def tearDownClass(cls):
pass
# 最后是針對模塊級別的Fixture測試夾具
# 在每個代碼文件執(zhí)行前后執(zhí)行的代碼結構
# 需要注意:模塊級別的需要寫在類的外邊直接定義函數即可
# 代碼文件之前
def setUpModule():
pass
# 代碼文件之后
def tearDownModule():
pass
# 下面我們采用一個用戶賬戶登錄的用例來簡單展示一下Fixture
import unittest
# 這些Fixture并沒有必要一次性全部書寫執(zhí)行,只需要書寫自己所需要的部分即可
# 例如下述的這個Moudle層級的Fixture其實是沒有必要執(zhí)行的,我們可以直接將他省略掉
"""""
# 代碼文件之前
def setUpModule():
print("文件執(zhí)行前")
# 代碼文件之后
def tearDownModule():
print("文件執(zhí)行后")
"""""
class TestLogin(unittest.TestCase):
# 在執(zhí)行該類前所需要調用的方法
@classmethod
def setUpClass(cls) -> None:
print('------打開瀏覽器')
# 在執(zhí)行該類后所需要調用的方法
@classmethod
def tearDownClass(cls) -> None:
print('------關閉瀏覽器')
# 每個測試方法執(zhí)行之前都會先調用的方法
def setUp(self):
print('輸入網址......')
# 每個測試方法執(zhí)行之后都會調用的方法
def tearDown(self) -> None:
print('關閉當前頁面......')
# 測試Case1
def test_1(self):
print('輸入正確用戶名密碼驗證碼,點擊登錄 1')
# 測試Case2
def test_2(self):
print('輸入錯誤用戶名密碼驗證碼,點擊登錄 2')
UnitTest進階部分
最后我們介紹UnitTest中的一些進階知識點
斷言
既然講到Python,那么斷言必然是我們所需要了解的部分:
- 斷言就是為了讓程序代替人工自動的判斷預期結果和實際結果是否相符
斷言的結果只分為兩種:
- True:用例通過
- False:用例不通過,并且拋出異常
我們在這里給出Python中所有經常使用到斷言語句:
| 斷言語法 | 斷言意義 |
|---|---|
| assertEqual(預期結果,實際結果) | 判斷是否相等 |
| assertNotEqual(預期結果,實際結果) | 判斷是否不等 |
| assertTrue(實際結果) | 判斷是否為True |
| assertFalse(實際結果) | 判斷是否為False |
| assertIsNone (實際結果) | 判斷是否為None |
| assertIsNotNone(實際結果) | 判斷是否不為None |
| assertIn(預期結果,實際結果) | 判斷是否包含 |
我們給出一個簡單的案例來展示斷言功能:
# 首先我們需要知道斷言其實就是一個判斷語句,用來判斷assert后面跟隨的內容是否為True
# 例如我們簡單的計算或比較其實也是一個斷言,不過上面的斷言語句是我們封裝后的結果
# 我們需要注意:如果我們使用到上面封裝的assert方法,那么我們需要采用self進行調用
import unittest
from tools import login
class TestLogin(unittest.TestCase):
# 其實下面的assert就是一個最基本的斷言判斷
def test_username_password_assert_ok(self):
res = login('admin', '123456')
assert res == "登錄成功"
# 判斷登錄:正確的用戶名和密碼: admin, 123456, 登錄成功
def test_username_password_ok(self):
self.assertEqual('登錄成功', login('admin', '123456'))
# 判斷登錄:錯誤的用戶名: root, 123456, 登錄失敗
def test_username_error(self):
self.assertEqual('登錄失敗', login('root', '123456'))
# 判斷登錄結果是否包含“失敗”,若包含即為成功
def test_username_password_error(self):
self.assertIn('失敗', login('aaa', '123123'))
參數化
下面我們來介紹UnitTest中的參數化:
-
在測試方法中, 使用變量來代替具體的測試數據, 然后使用傳參的方法將測試數據傳遞給方法的變量
-
用于減少代碼的重復書寫,減少我們的復雜操作
我們在工作中一般會將數據存放在JSON文件中或者從網頁中直接導出對應的JSON文件
下面我們來介紹參數化的兩種方法:
# unittest 框架本身是不支持 參數化, 想要使用參數化,需要安裝插件來完成
# console控制臺下載:pip install parameterized
# 參數化方法1
import unittest
from parameterized import parameterized
# 首先我們的參數可以存在放類中
# 我們一般采用列表中存在元組或列表來進行存儲:[(),(),()]或[[],[],[]]
data = [
('admin','123456','登陸成功'),
('admin','123123','登陸失敗'),
('errorAdmin','123456','登陸失敗'),
]
# 然后我們就可以定義參數化直接調用該data
class TestLogin(unittest.TestCase):
# 采用注解進行注入
@parameterized.expand(data)
def test_login(self,username,password,expect):
self.assertEqual(expect, login(username, password))
# 參數化方法2
# 首先我們需要單獨創(chuàng)建一個JSON類型的文件進行數據存儲
[
{
"desc": "正確的用戶名和密碼",
"username": "admin",
"password": "123456",
"expect": "登錄成功"
},
{
"desc": "錯誤的的用戶名",
"username": "root",
"password": "123456",
"expect": "登錄失敗"
},
{
"desc": "錯誤的的密碼",
"username": "admin",
"password": "123123",
"expect": "登錄失敗"
}
]
# 然后我們就可以到TestCase中去書寫參數化
# 首先我們需要導入該Json文件以及相關類
import json
import unittest
from parameterized import parameterized
from tools import login
# 然后我們需要有一個單獨的方法來獲取json文件并返回一個類似于我們前面所定義的date:
def build_data():
with open('data.json', encoding='utf-8') as f:
result = json.load(f) # [{}, {}, {}]
data = []
for i in result: # i {}
data.append((i.get('username'), i.get('password'), i.get('expect')))
return data
# 剩下的部分其實就和參數化1的操作相同的
class TestLogin(unittest.TestCase):
# 采用注解進行注入
@parameterized.expand(build_data())
def test_login(self,username,password,expect):
self.assertEqual(expect, login(username, password))
跳過測試
我們在前面的TestSuite或TestLoader里面一次性獲得所有Case并執(zhí)行,但有時我們也許不需要執(zhí)行某些Case:
- 對于一些未完成的或者不滿足測試條件的測試函數和測試類, 不想執(zhí)行,可以使用跳過
我們下面直接來介紹相關語法:
# 關于跳過測試其實就是一條注解我們就可以實現
# 其具體語法如下
# 直接將測試函數標記成跳過
@unittest.skip('跳過原因')
# 根據條件判斷測試函數是否跳過 , 判斷條件成立, 跳過
@unittest.skipIf(判斷條件, '跳過原因')
# 我們給出一條簡單的案例
import unittest
money = 29
class TestDemo(unittest.TestCase):
@unittest.skip('無跳過原因')
def test_1(self):
print('跳過測試')
@unittest.skipIf(version >= 30, '剩余金錢不足,無法購買')
def test_2(self):
money -= 30
print('剩余金錢足夠,可以購買')
def test_3(self):
print('可執(zhí)行方法')
結束語
這篇文章中詳細介紹了Python自帶的測試框架,因為是自帶框架,內容相對比較簡單,后續(xù)我們會學習pytest來加強自動化開發(fā)能力
附錄
下面給出我學習和書寫該篇文章的一些參考文章,大家也可以去查閱:
-
黑馬課程:11.Python-unittest 的組成_嗶哩嗶哩_bilibili
-
菜鳥教程:Python 基礎教程 | 菜鳥教程 (runoob.com)
總結
以上是生活随笔為你收集整理的一篇文章带你了解Python基础测试工具——UnitTest的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解码知识图谱:从核心概念到技术实战
- 下一篇: Python 潮流周刊#26:reque