Python自動(dòng)化測(cè)試框架:unittest、pytest、Selenium、requests和Pytest-BDD
前言
最近在學(xué)習(xí) Python 的過程中,我深刻認(rèn)識(shí)到自動(dòng)化測(cè)試的重要性。良好的測(cè)試可以幫助我們發(fā)現(xiàn)代碼中的錯(cuò)誤,提高代碼質(zhì)量,減少回歸 bug,同時(shí)也可以讓我們更自信地進(jìn)行代碼重構(gòu)。Python 擁有豐富的自動(dòng)化測(cè)試框架,如 unittest、pytest、selenium 等,這些框架各有特點(diǎn),適用于不同的測(cè)試場(chǎng)景。今天就來分享一下我的 Python 自動(dòng)化測(cè)試框架實(shí)戰(zhàn)經(jīng)驗(yàn),希望能幫到和我一樣的萌新們。
常見的 Python 自動(dòng)化測(cè)試框架
1. unittest
unittest 是 Python 標(biāo)準(zhǔn)庫中的測(cè)試框架,它提供了完整的測(cè)試工具集,包括測(cè)試發(fā)現(xiàn)、測(cè)試套件、測(cè)試運(yùn)行器等。
優(yōu)點(diǎn):
- 是 Python 標(biāo)準(zhǔn)庫的一部分,不需要額外安裝
- 提供了完整的測(cè)試框架功能
- 支持測(cè)試發(fā)現(xiàn)和測(cè)試套件
- 支持 setUp 和 tearDown 方法
缺點(diǎn):
- 語法相對(duì)繁瑣,需要編寫較多的 boilerplate 代碼
- 斷言方法有限
- 不支持參數(shù)化測(cè)試
適用場(chǎng)景:
- 簡(jiǎn)單的單元測(cè)試
- 對(duì)測(cè)試框架依賴要求較低的項(xiàng)目
- 學(xué)習(xí)測(cè)試基礎(chǔ)概念
示例:
import unittest
class TestStringMethods(unittest.TestCase):
def setUp(self):
# 測(cè)試前的準(zhǔn)備工作
self.test_string = "hello world"
def tearDown(self):
# 測(cè)試后的清理工作
pass
def test_upper(self):
self.assertEqual(self.test_string.upper(), "HELLO WORLD")
def test_isupper(self):
self.assertFalse(self.test_string.isupper())
self.assertTrue("HELLO".isupper())
def test_split(self):
s = "hello world"
self.assertEqual(s.split(), ["hello", "world"])
# 檢查分割后的結(jié)果是否符合預(yù)期
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
2. pytest
pytest 是一個(gè)功能強(qiáng)大的第三方測(cè)試框架,它提供了簡(jiǎn)潔的語法、豐富的插件生態(tài)和強(qiáng)大的測(cè)試功能。
優(yōu)點(diǎn):
- 語法簡(jiǎn)潔,不需要繼承特定的測(cè)試類
- 支持參數(shù)化測(cè)試
- 支持 fixtures 進(jìn)行測(cè)試準(zhǔn)備和清理
- 豐富的插件生態(tài)
- 強(qiáng)大的斷言能力
- 支持測(cè)試發(fā)現(xiàn)和測(cè)試過濾
缺點(diǎn):
- 需要額外安裝
- 對(duì)于初學(xué)者來說,插件體系可能有些復(fù)雜
適用場(chǎng)景:
- 單元測(cè)試
- 集成測(cè)試
- 功能測(cè)試
- 需要復(fù)雜測(cè)試場(chǎng)景的項(xiàng)目
示例:
import pytest
def test_upper():
assert "hello world".upper() == "HELLO WORLD"
def test_isupper():
assert not "hello world".isupper()
assert "HELLO".isupper()
def test_split():
s = "hello world"
assert s.split() == ["hello", "world"]
with pytest.raises(TypeError):
s.split(2)
# 參數(shù)化測(cè)試
@pytest.mark.parametrize("input, expected", [
("hello", "HELLO"),
("world", "WORLD"),
("test", "TEST"),
])
def test_parametrized_upper(input, expected):
assert input.upper() == expected
# fixtures
def pytest.fixture
def setup_test_string():
return "hello world"
def test_with_fixture(setup_test_string):
assert setup_test_string.upper() == "HELLO WORLD"
3. Selenium
Selenium 是一個(gè)用于 Web 應(yīng)用自動(dòng)化測(cè)試的工具,它可以模擬用戶在瀏覽器中的操作。
優(yōu)點(diǎn):
- 支持多種瀏覽器
- 可以模擬真實(shí)用戶操作
- 支持多種編程語言
- 強(qiáng)大的元素定位能力
缺點(diǎn):
- 運(yùn)行速度較慢
- 依賴瀏覽器和 WebDriver
- 容易受到頁面結(jié)構(gòu)變化的影響
適用場(chǎng)景:
- Web 應(yīng)用的功能測(cè)試
- 端到端測(cè)試
- 用戶界面測(cè)試
示例:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def test_google_search():
# 初始化瀏覽器
driver = webdriver.Chrome()
try:
# 打開 Google
driver.get("https://www.google.com")
# 定位搜索框并輸入搜索內(nèi)容
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("Python automation testing")
search_box.submit()
# 等待搜索結(jié)果加載
time.sleep(2)
# 驗(yàn)證搜索結(jié)果
assert "Python automation testing" in driver.title
# 定位搜索結(jié)果鏈接
search_results = driver.find_elements(By.CSS_SELECTOR, "h3")
assert len(search_results) > 0
finally:
# 關(guān)閉瀏覽器
driver.quit()
4. Requests
Requests 是一個(gè)用于 HTTP 請(qǐng)求的庫,它可以用于 API 測(cè)試。
優(yōu)點(diǎn):
- 語法簡(jiǎn)潔,使用方便
- 支持各種 HTTP 方法
- 支持會(huì)話管理
- 支持文件上傳和下載
缺點(diǎn):
- 只適用于 API 測(cè)試
- 不支持瀏覽器操作
適用場(chǎng)景:
- RESTful API 測(cè)試
- HTTP 服務(wù)測(cè)試
- 接口測(cè)試
示例:
import requests
def test_get_request():
response = requests.get("https://httpbin.org/get")
assert response.status_code == 200
assert "args" in response.json()
def test_post_request():
data = {"name": "test", "value": 123}
response = requests.post("https://httpbin.org/post", json=data)
assert response.status_code == 200
assert response.json()["json"] == data
def test_put_request():
data = {"name": "updated", "value": 456}
response = requests.put("https://httpbin.org/put", json=data)
assert response.status_code == 200
assert response.json()["json"] == data
def test_delete_request():
response = requests.delete("https://httpbin.org/delete")
assert response.status_code == 200
5. Pytest-BDD
Pytest-BDD 是基于行為驅(qū)動(dòng)開發(fā)(BDD)的測(cè)試框架,它使用 Gherkin 語言編寫測(cè)試場(chǎng)景。
優(yōu)點(diǎn):
- 測(cè)試場(chǎng)景使用自然語言描述,易于理解
- 支持 Given-When-Then 語法
- 與 pytest 集成,支持 pytest 的所有功能
缺點(diǎn):
- 需要學(xué)習(xí) Gherkin 語法
- 對(duì)于簡(jiǎn)單測(cè)試來說可能有些繁瑣
適用場(chǎng)景:
- 行為驅(qū)動(dòng)開發(fā)
- 需求驅(qū)動(dòng)的測(cè)試
- 團(tuán)隊(duì)協(xié)作測(cè)試
示例:
# features/example.feature
"""
Feature: Example feature
Scenario: Example scenario
Given I have a string "hello"
When I uppercase the string
Then the result should be "HELLO"
"""
# tests/test_example.py
from pytest_bdd import scenario, given, when, then
@scenario('features/example.feature', 'Example scenario')
def test_example():
pass
@given('I have a string "hello"')
def have_string():
return "hello"
@when('I uppercase the string')
def uppercase_string(have_string):
return have_string.upper()
@then('the result should be "HELLO"')
def check_result(uppercase_string):
assert uppercase_string == "HELLO"
實(shí)戰(zhàn)案例:使用 pytest 進(jìn)行單元測(cè)試
需求分析
我們將創(chuàng)建一個(gè)簡(jiǎn)單的計(jì)算器類,并使用 pytest 對(duì)其進(jìn)行單元測(cè)試。
實(shí)現(xiàn)步驟
- 創(chuàng)建計(jì)算器類:實(shí)現(xiàn)基本的算術(shù)操作
- 編寫測(cè)試用例:測(cè)試計(jì)算器的各種功能
- 運(yùn)行測(cè)試:使用 pytest 運(yùn)行測(cè)試并查看結(jié)果
- 分析測(cè)試結(jié)果:根據(jù)測(cè)試結(jié)果調(diào)整代碼
代碼實(shí)現(xiàn)
計(jì)算器類:
# calculator.py
class Calculator:
def add(self, a, b):
"""加法操作"""
return a + b
def subtract(self, a, b):
"""減法操作"""
return a - b
def multiply(self, a, b):
"""乘法操作"""
return a * b
def divide(self, a, b):
"""除法操作"""
if b == 0:
raise ValueError("除數(shù)不能為零")
return a / b
測(cè)試用例:
# test_calculator.py
import pytest
from calculator import Calculator
@pytest.fixture
def calculator():
return Calculator()
def test_add(calculator):
assert calculator.add(1, 2) == 3
assert calculator.add(-1, 1) == 0
assert calculator.add(0, 0) == 0
assert calculator.add(1.5, 2.5) == 4.0
def test_subtract(calculator):
assert calculator.subtract(5, 3) == 2
assert calculator.subtract(1, 5) == -4
assert calculator.subtract(0, 0) == 0
assert calculator.subtract(4.5, 2.5) == 2.0
def test_multiply(calculator):
assert calculator.multiply(2, 3) == 6
assert calculator.multiply(-1, 5) == -5
assert calculator.multiply(0, 100) == 0
assert calculator.multiply(2.5, 4) == 10.0
def test_divide(calculator):
assert calculator.divide(10, 2) == 5
assert calculator.divide(5, 2) == 2.5
assert calculator.divide(-10, 2) == -5
with pytest.raises(ValueError, match="除數(shù)不能為零"):
calculator.divide(10, 0)
# 參數(shù)化測(cè)試
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(-1, 1, 0),
(0, 0, 0),
(1.5, 2.5, 4.0),
])
def test_parametrized_add(calculator, a, b, expected):
assert calculator.add(a, b) == expected
運(yùn)行測(cè)試:
# 安裝 pytest pip install pytest # 運(yùn)行測(cè)試 pytest test_calculator.py -v # 運(yùn)行特定測(cè)試 pytest test_calculator.py::test_add -v # 運(yùn)行帶有特定標(biāo)記的測(cè)試 pytest test_calculator.py -m slow -v # 生成測(cè)試報(bào)告 pytest test_calculator.py --html=report.html
實(shí)戰(zhàn)案例:使用 Selenium 進(jìn)行 Web 自動(dòng)化測(cè)試
需求分析
我們將使用 Selenium 測(cè)試一個(gè)簡(jiǎn)單的登錄功能,包括輸入用戶名和密碼,點(diǎn)擊登錄按鈕,驗(yàn)證登錄是否成功。
實(shí)現(xiàn)步驟
- 安裝 Selenium:安裝 Selenium 和 WebDriver
- 編寫測(cè)試用例:測(cè)試登錄功能
- 運(yùn)行測(cè)試:使用 pytest 運(yùn)行測(cè)試并查看結(jié)果
- 分析測(cè)試結(jié)果:根據(jù)測(cè)試結(jié)果調(diào)整測(cè)試用例
代碼實(shí)現(xiàn)
測(cè)試用例:
# test_login.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pytest
@pytest.fixture
def driver():
# 初始化瀏覽器
driver = webdriver.Chrome()
driver.implicitly_wait(10)
yield driver
# 測(cè)試結(jié)束后關(guān)閉瀏覽器
driver.quit()
def test_login_success(driver):
# 打開登錄頁面
driver.get("https://example.com/login")
# 輸入用戶名和密碼
username_input = driver.find_element(By.ID, "username")
password_input = driver.find_element(By.ID, "password")
username_input.send_keys("testuser")
password_input.send_keys("testpassword")
# 點(diǎn)擊登錄按鈕
login_button = driver.find_element(By.ID, "login-button")
login_button.click()
# 等待登錄成功頁面加載
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "welcome-message"))
)
# 驗(yàn)證登錄成功
welcome_message = driver.find_element(By.ID, "welcome-message")
assert "Welcome" in welcome_message.text
def test_login_failure(driver):
# 打開登錄頁面
driver.get("https://example.com/login")
# 輸入錯(cuò)誤的用戶名和密碼
username_input = driver.find_element(By.ID, "username")
password_input = driver.find_element(By.ID, "password")
username_input.send_keys("testuser")
password_input.send_keys("wrongpassword")
# 點(diǎn)擊登錄按鈕
login_button = driver.find_element(By.ID, "login-button")
login_button.click()
# 等待錯(cuò)誤消息顯示
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "error-message"))
)
# 驗(yàn)證錯(cuò)誤消息
error_message = driver.find_element(By.ID, "error-message")
assert "Invalid username or password" in error_message.text
運(yùn)行測(cè)試:
# 安裝 Selenium pip install selenium # 下載 WebDriver(ChromeDriver)并添加到 PATH # 運(yùn)行測(cè)試 pytest test_login.py -v
實(shí)戰(zhàn)案例:使用 Requests 進(jìn)行 API 測(cè)試
需求分析
我們將使用 Requests 測(cè)試一個(gè)簡(jiǎn)單的 RESTful API,包括 GET、POST、PUT 和 DELETE 請(qǐng)求。
實(shí)現(xiàn)步驟
- 安裝 Requests:安裝 Requests 庫
- 編寫測(cè)試用例:測(cè)試 API 的各種功能
- 運(yùn)行測(cè)試:使用 pytest 運(yùn)行測(cè)試并查看結(jié)果
- 分析測(cè)試結(jié)果:根據(jù)測(cè)試結(jié)果調(diào)整測(cè)試用例
代碼實(shí)現(xiàn)
測(cè)試用例:
# test_api.py
import requests
import pytest
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_get_posts():
"""測(cè)試獲取帖子列表"""
response = requests.get(f"{BASE_URL}/posts")
assert response.status_code == 200
posts = response.json()
assert isinstance(posts, list)
assert len(posts) > 0
def test_get_post():
"""測(cè)試獲取單個(gè)帖子"""
post_id = 1
response = requests.get(f"{BASE_URL}/posts/{post_id}")
assert response.status_code == 200
post = response.json()
assert post["id"] == post_id
def test_create_post():
"""測(cè)試創(chuàng)建帖子"""
data = {
"title": "Test Post",
"body": "This is a test post",
"userId": 1
}
response = requests.post(f"{BASE_URL}/posts", json=data)
assert response.status_code == 201
post = response.json()
assert post["title"] == data["title"]
assert post["body"] == data["body"]
assert post["userId"] == data["userId"]
def test_update_post():
"""測(cè)試更新帖子"""
post_id = 1
data = {
"title": "Updated Test Post",
"body": "This is an updated test post",
"userId": 1
}
response = requests.put(f"{BASE_URL}/posts/{post_id}", json=data)
assert response.status_code == 200
post = response.json()
assert post["id"] == post_id
assert post["title"] == data["title"]
assert post["body"] == data["body"]
def test_delete_post():
"""測(cè)試刪除帖子"""
post_id = 1
response = requests.delete(f"{BASE_URL}/posts/{post_id}")
assert response.status_code == 200
運(yùn)行測(cè)試:
# 安裝 Requests pip install requests # 運(yùn)行測(cè)試 pytest test_api.py -v
測(cè)試最佳實(shí)踐
測(cè)試隔離:每個(gè)測(cè)試應(yīng)該獨(dú)立運(yùn)行,不依賴于其他測(cè)試的狀態(tài)
測(cè)試覆蓋:確保測(cè)試覆蓋主要的功能和邊界情況
測(cè)試命名:使用清晰、描述性的測(cè)試名稱
測(cè)試數(shù)據(jù):使用合適的測(cè)試數(shù)據(jù),包括正常情況和邊界情況
測(cè)試速度:保持測(cè)試運(yùn)行速度快,便于頻繁運(yùn)行
測(cè)試維護(hù):定期更新測(cè)試,確保測(cè)試與代碼同步
測(cè)試報(bào)告:生成詳細(xì)的測(cè)試報(bào)告,便于分析測(cè)試結(jié)果
持續(xù)集成:在持續(xù)集成系統(tǒng)中運(yùn)行測(cè)試,確保代碼質(zhì)量
常見問題與解決方案
1. 測(cè)試運(yùn)行慢
問題:測(cè)試運(yùn)行速度慢,影響開發(fā)效率。
解決方案:
- 減少測(cè)試中的網(wǎng)絡(luò)請(qǐng)求和數(shù)據(jù)庫操作
- 使用 mock 模擬外部依賴
- 并行運(yùn)行測(cè)試
- 只運(yùn)行相關(guān)的測(cè)試
2. 測(cè)試不穩(wěn)定
問題:測(cè)試有時(shí)通過,有時(shí)失敗,不穩(wěn)定。
解決方案:
- 確保測(cè)試環(huán)境一致
- 避免測(cè)試之間的依賴
- 處理測(cè)試中的異步操作
- 增加適當(dāng)?shù)牡却龝r(shí)間
3. 測(cè)試代碼維護(hù)困難
問題:測(cè)試代碼難以維護(hù),隨著代碼的變化需要頻繁更新。
解決方案:
- 使用 fixtures 減少重復(fù)代碼
- 采用頁面對(duì)象模式(Page Object Pattern)管理 UI 測(cè)試
- 保持測(cè)試代碼簡(jiǎn)潔明了
- 定期重構(gòu)測(cè)試代碼
4. 測(cè)試覆蓋不足
問題:測(cè)試覆蓋不足,無法發(fā)現(xiàn)所有的 bug。
解決方案:
- 使用測(cè)試覆蓋工具(如 coverage.py)分析覆蓋情況
- 針對(duì)邊界情況和異常情況編寫測(cè)試
- 定期審查測(cè)試覆蓋報(bào)告
總結(jié)
Python 擁有豐富的自動(dòng)化測(cè)試框架,每個(gè)框架都有其特點(diǎn)和適用場(chǎng)景。unittest 是 Python 標(biāo)準(zhǔn)庫的一部分,適合簡(jiǎn)單的單元測(cè)試;pytest 提供了簡(jiǎn)潔的語法和豐富的功能,適合各種測(cè)試場(chǎng)景;Selenium 用于 Web 應(yīng)用的自動(dòng)化測(cè)試;Requests 用于 API 測(cè)試;Pytest-BDD 用于行為驅(qū)動(dòng)開發(fā)。
通過使用這些測(cè)試框架,我們可以編寫高質(zhì)量的測(cè)試代碼,提高代碼質(zhì)量,減少回歸 bug,同時(shí)也可以讓我們更自信地進(jìn)行代碼重構(gòu)。作為一個(gè)從后端轉(zhuǎn) Rust 的萌新,我在學(xué)習(xí) Python 自動(dòng)化測(cè)試的過程中遇到了一些挑戰(zhàn),也學(xué)到了很多東西。通過不斷學(xué)習(xí)和實(shí)踐,我相信我能夠編寫更加有效的測(cè)試代碼。
到此這篇關(guān)于Python自動(dòng)化測(cè)試框架:unittest、pytest、Selenium、requests和Pytest-BDD的文章就介紹到這了,更多相關(guān)Python自動(dòng)化測(cè)試框架項(xiàng)目實(shí)戰(zhàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python+Selenium瀏覽器自動(dòng)化測(cè)試與網(wǎng)頁自動(dòng)登錄
- Python使用Selenium實(shí)現(xiàn)Web自動(dòng)化的完整指南
- 從零開始學(xué)習(xí)Python Selenium瀏覽器元素定位與實(shí)戰(zhàn)技巧
- Python使用Selenium將網(wǎng)頁保存為圖片或PDF
- 從入門到精通解析Python Selenium如何模擬瀏覽器操作
- Python使用Selenium實(shí)現(xiàn)自動(dòng)化網(wǎng)頁批量錄入
- Python Selenium 滾動(dòng)到特定元素的幾種實(shí)現(xiàn)方法
- python中selenium安裝配置與使用示例
- Python使用Selenium檢查元素是否存在的方法
- python利用Selenium搭建免費(fèi)Web搜索API服務(wù)的方法
相關(guān)文章
解決python gdal投影坐標(biāo)系轉(zhuǎn)換的問題
今天小編就為大家分享一篇解決python gdal投影坐標(biāo)系轉(zhuǎn)換的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-01-01
使用Python實(shí)現(xiàn)調(diào)整Excel中的行列順序
調(diào)整Excel?行列順序指的是改變工作表中行或列的位置,以便更好地展示和分析數(shù)據(jù),本文將介紹如何通過Python高效地調(diào)整Excel?行列順序,感興趣的可以了解下2025-01-01
使用Termux在手機(jī)上運(yùn)行Python的詳細(xì)過程
這篇文章主要介紹了使用Termux在手機(jī)上運(yùn)行Python的詳細(xì)過程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Python基礎(chǔ)之logging模塊知識(shí)總結(jié)
用Python寫代碼的時(shí)候,在想看的地方寫個(gè)print xx 就能在控制臺(tái)上顯示打印信息,這樣子就能知道它是什么了,但是當(dāng)我需要看大量的地方或者在一個(gè)文件中查看的時(shí)候,這時(shí)候print就不大方便了,所以Python引入了logging模塊來記錄我想要的信息,需要的朋友可以參考下2021-05-05

