Selenium 自动化测试教程
第1章 项目创建与环境搭建
1.1 环境准备
- 操作系统:Windows 10/11
- Python 版本:3.12+
- 浏览器:Microsoft Edge
1.2 创建虚拟环境
powershell
# 创建虚拟环境
python -m venv .venv
# 激活虚拟环境
.venv\Scripts\Activate.ps1
# 升级 pip
python -m pip install --upgrade pip
1.3 安装依赖
powershell
pip install selenium webdriver-manager
1.4 项目结构
test/
├── .venv/ # 虚拟环境
├── specified_directory/ # 运费计算测试
├── ecommerce_cart/ # 电商购物车测试
├── online_registration/ # 在线注册测试
├── flight_search/ # 航班查询测试
├── bank_transfer/ # 银行转账测试
├── online_education/ # 在线教育课程报名测试
└── selenium_automation_tutorial.md # 本教程
第2章 基础案例:运费计算
2.1 场景说明
某快递公司官网提供"运费预估"功能。用户需输入寄件重量(公斤),选择包裹类型(普通件/易碎品),并勾选附加服务(是否保价、是否加急)。点击"计算运费"后,页面将显示预估运费。
2.2 网页实现
创建 shipping_calculator.html 文件:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>运费预估</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.radio-group, .checkbox-group {
margin-top: 5px;
}
.radio-group label, .checkbox-group label {
display: inline;
margin-right: 15px;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#result {
margin-top: 20px;
font-size: 24px;
font-weight: bold;
color: #333;
}
</style>
</head>
<body>
<h1>运费预估</h1>
<form id="shipping-form">
<div class="form-group">
<label for="weight">寄件重量(公斤):</label>
<input type="text" id="weight" name="weight" placeholder="请输入重量">
</div>
<div class="form-group">
<label>包裹类型:</label>
<div class="radio-group">
<input type="radio" id="normal" name="package_type" value="normal" checked>
<label for="normal">普通件</label>
<input type="radio" id="fragile" name="package_type" value="fragile">
<label for="fragile">易碎品</label>
</div>
</div>
<div class="form-group">
<label>附加服务:</label>
<div class="checkbox-group">
<input type="checkbox" id="insurance" name="insurance" value="1">
<label for="insurance">保价服务</label>
<input type="checkbox" id="express" name="express" value="1">
<label for="express">加急配送</label>
</div>
</div>
<button type="button" id="calculate">计算运费</button>
</form>
<div id="result"></div>
<script>
document.getElementById('calculate').addEventListener('click', function() {
// 模拟计算耗时约1秒
setTimeout(function() {
const result = document.getElementById('result');
result.textContent = '¥45.00';
}, 1000);
});
</script>
</body>
</html>
2.3 测试脚本实现
创建 test_shipping_cost.py 文件:
python
import time
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
# 创建 Edge 浏览器实例
driver = webdriver.Edge()
# 浏览器最大化显示
driver.maximize_window()
# 访问页面(根据任务要求,运费计算文件存放在specified_directory目录中)
driver.get("http://localhost:8000/specified_directory/shipping_calculator.html")
try:
# 1. 定位寄件重量输入框,清空并输入5
weight_input = driver.find_element(By.ID, "weight")
weight_input.clear()
weight_input.send_keys("5")
# 2. 定位"易碎品"单选按钮,并选中
fragile_radio = driver.find_element(By.XPATH, "//input[@value='fragile']")
fragile_radio.click()
# 3. 定位"保价服务"复选框,并勾选
insurance_checkbox = driver.find_element(By.XPATH, "//input[@id='insurance']")
insurance_checkbox.click()
# 4. 定位"加急配送"复选框,并勾选
express_checkbox = driver.find_element(By.XPATH, "//input[@id='express']")
express_checkbox.click()
# 5. 定位"计算运费"按钮,并点击
calculate_button = driver.find_element(By.XPATH, "//button[text()='计算运费']")
calculate_button.click()
# 6. 使用显式等待,等待运费结果显示区域加载完成
wait = WebDriverWait(driver, 10)
result_element = wait.until(EC.text_to_be_present_in_element((By.XPATH, "//div[contains(@id,'result')]"), "¥"))
result_element = driver.find_element(By.XPATH, "//div[contains(@id,'result')]")
# 7. 获取显示的运费金额,预期结果为 ¥45.00,并进行断言验证
fee_text = result_element.text.strip()
expected_fee = "¥45.00"
assert fee_text == expected_fee, f"期望运费为 {expected_fee},但实际为 {fee_text}"
print("运费计算正确!")
except Exception as e:
print(f"测试失败:{e}")
finally:
# 测试完成后等待15秒再关闭浏览器
time.sleep(15)
driver.quit()
第3章 在线注册功能测试
3.1 场景说明
用户访问网站注册页面,填写注册信息,提交注册表单,验证注册成功。
3.2 网页实现
创建 online_registration.html 文件:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在线注册测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"], input[type="email"], input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ddd;
border-radius: 4px;
}
.checkbox-group {
margin-top: 10px;
}
.checkbox-group label {
display: inline;
font-weight: normal;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 4px;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
#captcha-section {
display: flex;
gap: 10px;
align-items: center;
}
#captcha-btn {
background-color: #2196F3;
padding: 8px 12px;
font-size: 14px;
}
#captcha-btn:hover {
background-color: #0b7dda;
}
#captcha-btn:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.message {
margin: 20px 0;
padding: 10px;
border-radius: 4px;
font-weight: bold;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
</head>
<body>
<h1>在线注册测试</h1>
<div id="message" class="message" style="display: none;"></div>
<form id="registration-form">
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
</div>
<div class="form-group">
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" placeholder="请输入邮箱" required>
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" id="password" name="password" placeholder="请输入密码" required>
</div>
<div class="form-group">
<label for="confirm-password">确认密码:</label>
<input type="password" id="confirm-password" name="confirm-password" placeholder="请再次输入密码" required>
</div>
<div class="form-group">
<label for="captcha">验证码:</label>
<div id="captcha-section">
<input type="text" id="captcha" name="captcha" placeholder="请输入验证码" required>
<button type="button" id="captcha-btn">获取验证码</button>
</div>
</div>
<div class="checkbox-group">
<input type="checkbox" id="agree" name="agree" required>
<label for="agree">我同意<a href="#">用户协议</a>和<a href="#">隐私政策</a></label>
</div>
<button type="submit">注册</button>
</form>
<script>
// 验证码倒计时
let countdown = 0;
let timer = null;
// 验证码按钮点击事件
document.getElementById('captcha-btn').addEventListener('click', function() {
const email = document.getElementById('email').value;
if (!email) {
showMessage('请先输入邮箱', 'error');
return;
}
// 开始倒计时
startCountdown(60);
showMessage('验证码已发送到您的邮箱,验证码为:123456', 'success');
});
// 倒计时函数
function startCountdown(seconds) {
countdown = seconds;
const btn = document.getElementById('captcha-btn');
btn.disabled = true;
btn.textContent = `${countdown}秒后重新获取`;
if (timer) {
clearInterval(timer);
}
timer = setInterval(() => {
countdown--;
if (countdown <= 0) {
clearInterval(timer);
btn.disabled = false;
btn.textContent = '获取验证码';
} else {
btn.textContent = `${countdown}秒后重新获取`;
}
}, 1000);
}
// 显示消息
function showMessage(text, type) {
const message = document.getElementById('message');
message.textContent = text;
message.className = `message ${type}`;
message.style.display = 'block';
}
// 注册表单提交事件
document.getElementById('registration-form').addEventListener('submit', function(e) {
e.preventDefault();
const username = document.getElementById('username').value;
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm-password').value;
const captcha = document.getElementById('captcha').value;
const agree = document.getElementById('agree').checked;
// 简单验证
if (!username || !email || !password || !confirmPassword || !captcha || !agree) {
showMessage('请填写所有必填字段', 'error');
return;
}
if (password !== confirmPassword) {
showMessage('两次输入的密码不一致', 'error');
return;
}
if (captcha !== '123456') {
showMessage('验证码错误', 'error');
return;
}
// 模拟注册成功
setTimeout(() => {
showMessage('注册成功!', 'success');
// 清空表单
this.reset();
}, 1000);
});
</script>
</body>
</html>
3.3 测试脚本实现
创建 test_online_registration.py 文件:
python
import time
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
# 创建 Edge 浏览器实例
driver = webdriver.Edge()
# 浏览器最大化显示
driver.maximize_window()
try:
# 访问页面
driver.get("http://localhost:8000/online_registration/online_registration.html")
# 输入用户名
username_input = driver.find_element(By.ID, "username")
username_input.send_keys("testuser123")
# 输入邮箱
email_input = driver.find_element(By.ID, "email")
email_input.send_keys("test@example.com")
# 输入密码
password_input = driver.find_element(By.ID, "password")
password_input.send_keys("Password123!")
# 输入确认密码
confirm_password_input = driver.find_element(By.ID, "confirm-password")
confirm_password_input.send_keys("Password123!")
# 点击"获取验证码"按钮
captcha_btn = driver.find_element(By.ID, "captcha-btn")
captcha_btn.click()
# 输入验证码
captcha_input = driver.find_element(By.ID, "captcha")
captcha_input.send_keys("123456")
# 勾选"同意用户协议"复选框
agree_checkbox = driver.find_element(By.ID, "agree")
agree_checkbox.click()
# 点击"注册"按钮
register_btn = driver.find_element(By.XPATH, "//button[text()='注册']")
register_btn.click()
# 等待注册成功提示
success_message = WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "message"), "注册成功")
)
# 断言验证
actual_message = driver.find_element(By.ID, "message").text
expected_message = "注册成功!"
assert actual_message == expected_message, f"期望消息为 {expected_message},但实际为 {actual_message}"
print(f"测试通过!实际消息:{actual_message}")
except Exception as e:
print(f"测试失败:{e}")
finally:
# 测试完成后等待15秒再关闭浏览器
time.sleep(15)
driver.quit()
第4章 电商购物车测试
4.1 场景说明
用户浏览商品,添加商品到购物车,修改商品数量,选择配送方式,计算总价。
4.2 网页实现
创建 ecommerce_cart.html 文件:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电商购物车测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.product-list {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.product {
border: 1px solid #ddd;
padding: 15px;
width: 200px;
}
.cart {
border: 1px solid #ddd;
padding: 20px;
margin-top: 30px;
}
.cart-item {
display: flex;
justify-content: space-between;
margin: 10px 0;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.quantity-control {
display: flex;
align-items: center;
gap: 10px;
}
.shipping-options {
margin: 20px 0;
}
.total {
font-size: 20px;
font-weight: bold;
margin-top: 20px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 8px 16px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<h1>电商购物车测试</h1>
<div class="product-list">
<div class="product">
<h3>笔记本电脑</h3>
<p>价格:¥5000.00</p>
<button class="add-to-cart" data-name="笔记本电脑" data-price="5000">加入购物车</button>
</div>
<div class="product">
<h3>智能手机</h3>
<p>价格:¥3000.00</p>
<button class="add-to-cart" data-name="智能手机" data-price="3000">加入购物车</button>
</div>
<div class="product">
<h3>无线耳机</h3>
<p>价格:¥500.00</p>
<button class="add-to-cart" data-name="无线耳机" data-price="500">加入购物车</button>
</div>
</div>
<div class="cart">
<h2>购物车</h2>
<div id="cart-items"></div>
<div class="shipping-options">
<h3>配送方式:</h3>
<label>
<input type="radio" name="shipping" value="standard" checked> 标准配送 (¥0.00)
</label><br>
<label>
<input type="radio" name="shipping" value="express"> 顺丰快递 (¥20.00)
</label>
</div>
<div class="total">
总价:<span id="total-price">¥0.00</span>
</div>
</div>
<script>
// 购物车数据
let cart = [];
let shippingCost = 0;
// 添加到购物车
document.querySelectorAll('.add-to-cart').forEach(button => {
button.addEventListener('click', function() {
const name = this.dataset.name;
const price = parseFloat(this.dataset.price);
// 检查商品是否已在购物车中
const existingItem = cart.find(item => item.name === name);
if (existingItem) {
existingItem.quantity += 1;
} else {
cart.push({ name, price, quantity: 1 });
}
updateCart();
});
});
// 更新购物车显示
function updateCart() {
const cartItems = document.getElementById('cart-items');
cartItems.innerHTML = '';
let subtotal = 0;
cart.forEach(item => {
const itemElement = document.createElement('div');
itemElement.className = 'cart-item';
const itemPrice = item.price * item.quantity;
subtotal += itemPrice;
itemElement.innerHTML = `
<div>
<h4>${item.name}</h4>
<p>单价:¥${item.price.toFixed(2)}</p>
</div>
<div class="quantity-control">
<button class="decrease" data-name="${item.name}">-</button>
<span>${item.quantity}</span>
<button class="increase" data-name="${item.name}">+</button>
</div>
<div>¥${itemPrice.toFixed(2)}</div>
`;
cartItems.appendChild(itemElement);
});
// 添加数量控制事件
addQuantityControls();
// 计算总价
const total = subtotal + shippingCost;
document.getElementById('total-price').textContent = `¥${total.toFixed(2)}`;
}
// 添加数量控制事件
function addQuantityControls() {
// 减少数量
document.querySelectorAll('.decrease').forEach(button => {
button.addEventListener('click', function() {
const name = this.dataset.name;
const item = cart.find(item => item.name === name);
if (item && item.quantity > 1) {
item.quantity -= 1;
updateCart();
}
});
});
// 增加数量
document.querySelectorAll('.increase').forEach(button => {
button.addEventListener('click', function() {
const name = this.dataset.name;
const item = cart.find(item => item.name === name);
if (item) {
item.quantity += 1;
updateCart();
}
});
});
}
// 配送方式选择
document.querySelectorAll('input[name="shipping"]').forEach(radio => {
radio.addEventListener('change', function() {
shippingCost = this.value === 'express' ? 20 : 0;
updateCart();
});
});
</script>
</body>
</html>
4.3 测试脚本实现
创建 test_ecommerce_cart.py 文件:
python
import time
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
# 创建 Edge 浏览器实例
driver = webdriver.Edge()
# 浏览器最大化显示
driver.maximize_window()
try:
# 访问页面
driver.get("http://localhost:8000/ecommerce_cart/ecommerce_cart.html")
# 点击"笔记本电脑"的"加入购物车"按钮
laptop_add_button = driver.find_element(By.XPATH, "//h3[text()='笔记本电脑']/following-sibling::button")
laptop_add_button.click()
# 点击"增加"按钮将数量从1改为2
increase_button = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'cart-item')]//button[text()='+']"))
)
increase_button.click()
# 选择"顺丰快递"配送方式
express_shipping = driver.find_element(By.XPATH, "//input[@value='express']")
express_shipping.click()
# 等待总价更新
total_price = WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "total-price"), "¥10020.00")
)
# 断言验证
actual_total = driver.find_element(By.ID, "total-price").text
expected_total = "¥10020.00"
assert actual_total == expected_total, f"期望总价为 {expected_total},但实际为 {actual_total}"
print(f"测试通过!实际总价:{actual_total}")
except Exception as e:
print(f"测试失败:{e}")
finally:
# 测试完成后等待15秒再关闭浏览器
time.sleep(15)
driver.quit()
第5章 航班查询功能测试
5.1 场景说明
用户访问机票预订网站,输入出发地、目的地、日期,查询航班信息,选择航班查看详情。
5.2 网页实现
创建 flight_search.html 文件:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>航班查询测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.search-form {
background-color: #f0f8ff;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.form-row {
display: flex;
gap: 20px;
margin-bottom: 15px;
align-items: center;
}
.form-group {
flex: 1;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select, button {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
.trip-type {
display: flex;
gap: 10px;
}
.trip-type label {
display: inline;
font-weight: normal;
}
#flight-results {
margin-top: 20px;
}
.flight-item {
border: 1px solid #ddd;
padding: 20px;
margin-bottom: 15px;
border-radius: 8px;
}
.flight-item:hover {
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.flight-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.flight-info {
display: flex;
justify-content: space-between;
margin: 10px 0;
}
.flight-time {
font-size: 24px;
font-weight: bold;
}
.flight-route {
display: flex;
align-items: center;
gap: 10px;
}
.flight-duration {
color: #666;
}
.view-details {
background-color: #2196F3;
color: white;
border: none;
padding: 8px 16px;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
}
.view-details:hover {
background-color: #0b7dda;
}
.flight-details {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eee;
display: none;
}
.flight-details.show {
display: block;
}
.message {
margin: 20px 0;
padding: 10px;
border-radius: 4px;
font-weight: bold;
}
.loading {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
</style>
</head>
<body>
<h1>航班查询测试</h1>
<div id="message" class="message" style="display: none;"></div>
<div class="search-form">
<h2>航班查询</h2>
<div class="form-row">
<div class="trip-type">
<input type="radio" id="one-way" name="trip-type" value="one-way" checked>
<label for="one-way">单程</label>
<input type="radio" id="round-trip" name="trip-type" value="round-trip">
<label for="round-trip">往返</label>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="from">出发地:</label>
<select id="from" name="from">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="成都">成都</option>
</select>
</div>
<div class="form-group">
<label for="to">目的地:</label>
<select id="to" name="to">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="成都">成都</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="departure-date">出发日期:</label>
<input type="date" id="departure-date" name="departure-date" required>
</div>
<div class="form-group" id="return-date-group" style="display: none;">
<label for="return-date">返回日期:</label>
<input type="date" id="return-date" name="return-date">
</div>
</div>
<button type="button" id="search-flights">搜索航班</button>
</div>
<div id="flight-results"></div>
<script>
// 设置默认出发日期为明天
document.addEventListener('DOMContentLoaded', function() {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const formattedDate = tomorrow.toISOString().split('T')[0];
document.getElementById('departure-date').value = formattedDate;
});
// 往返/单程切换
document.querySelectorAll('input[name="trip-type"]').forEach(radio => {
radio.addEventListener('change', function() {
const returnDateGroup = document.getElementById('return-date-group');
if (this.value === 'round-trip') {
returnDateGroup.style.display = 'block';
} else {
returnDateGroup.style.display = 'none';
}
});
});
// 航班数据
const flightData = [
{
id: 1,
airline: '中国国际航空',
flightNumber: 'CA1234',
departureCity: '北京',
departureTime: '08:00',
arrivalCity: '上海',
arrivalTime: '10:30',
duration: '2小时30分钟',
price: '¥800.00',
details: '机型:波音737-800,准点率:95%,行李额:20kg'
},
{
id: 2,
airline: '东方航空',
flightNumber: 'MU5678',
departureCity: '北京',
departureTime: '10:00',
arrivalCity: '上海',
arrivalTime: '12:30',
duration: '2小时30分钟',
price: '¥750.00',
details: '机型:空客A320,准点率:92%,行李额:20kg'
},
{
id: 3,
airline: '南方航空',
flightNumber: 'CZ9012',
departureCity: '北京',
departureTime: '12:00',
arrivalCity: '上海',
arrivalTime: '14:30',
duration: '2小时30分钟',
price: '¥780.00',
details: '机型:波音737-800,准点率:90%,行李额:20kg'
}
];
// 显示消息
function showMessage(text, type) {
const message = document.getElementById('message');
message.textContent = text;
message.className = `message ${type}`;
message.style.display = 'block';
}
// 隐藏消息
function hideMessage() {
const message = document.getElementById('message');
message.style.display = 'none';
}
// 搜索航班
document.getElementById('search-flights').addEventListener('click', function() {
// 显示加载中消息
showMessage('正在搜索航班...', 'loading');
// 模拟网络延迟
setTimeout(() => {
hideMessage();
displayFlights();
}, 1500);
});
// 显示航班列表
function displayFlights() {
const resultsContainer = document.getElementById('flight-results');
resultsContainer.innerHTML = '';
if (flightData.length === 0) {
resultsContainer.innerHTML = '<p>没有找到符合条件的航班。</p>';
return;
}
flightData.forEach(flight => {
const flightElement = document.createElement('div');
flightElement.className = 'flight-item';
flightElement.innerHTML = `
<div class="flight-header">
<div>
<h3>${flight.airline} ${flight.flightNumber}</h3>
</div>
<div style="font-size: 20px; font-weight: bold;">${flight.price}</div>
</div>
<div class="flight-info">
<div class="flight-time">${flight.departureTime}</div>
<div class="flight-route">
<strong>${flight.departureCity}</strong>
<span>→</span>
<strong>${flight.arrivalCity}</strong>
</div>
<div class="flight-time">${flight.arrivalTime}</div>
</div>
<div class="flight-info">
<div></div>
<div class="flight-duration">${flight.duration}</div>
<div></div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<button class="view-details" onclick="toggleDetails(${flight.id})">查看详情</button>
</div>
<div id="details-${flight.id}" class="flight-details">
<p>${flight.details}</p>
</div>
`;
resultsContainer.appendChild(flightElement);
});
}
// 切换详情显示
window.toggleDetails = function(flightId) {
const detailsElement = document.getElementById(`details-${flightId}`);
detailsElement.classList.toggle('show');
}
</script>
</body>
</html>
5.3 测试脚本实现
创建 test_flight_search.py 文件:
python
import time
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
# 创建 Edge 浏览器实例
driver = webdriver.Edge()
# 浏览器最大化显示
driver.maximize_window()
try:
# 访问页面
driver.get("http://localhost:8000/flight_search/flight_search.html")
# 选择目的地为上海
to_select = driver.find_element(By.ID, "to")
to_select.send_keys("上海")
# 点击"搜索航班"按钮
search_button = driver.find_element(By.ID, "search-flights")
search_button.click()
# 等待航班列表加载
flight_items = WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.XPATH, "//div[@class='flight-item']"))
)
# 选择第一个航班,点击"查看详情"
view_details_button = flight_items[0].find_element(By.XPATH, ".//button[text()='查看详情']")
view_details_button.click()
# 等待航班详情显示
flight_details = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[@class='flight-details show']"))
)
# 断言验证
actual_details = flight_details.text
expected_keyword = "机型:"
assert expected_keyword in actual_details, f"期望详情中包含 '{expected_keyword}',但实际为 '{actual_details}'"
print(f"测试通过!航班详情:{actual_details}")
except Exception as e:
print(f"测试失败:{e}")
finally:
# 测试完成后等待15秒再关闭浏览器
time.sleep(15)
driver.quit()
第6章 银行转账功能测试
6.1 场景说明
用户登录网上银行,进行账户间转账,输入转账金额,验证转账结果。
6.2 网页实现
创建 bank_transfer.html 文件:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>银行转账测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.transfer-form {
background-color: #f0f8ff;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
select, input, button {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
.message {
margin: 20px 0;
padding: 10px;
border-radius: 4px;
font-weight: bold;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.loading {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
</style>
</head>
<body>
<h1>银行转账测试</h1>
<div id="message" class="message" style="display: none;"></div>
<div class="transfer-form">
<h2>账户转账</h2>
<div class="form-group">
<label for="from-account">转出账户:</label>
<select id="from-account" name="from-account">
<option value="1234567890">储蓄账户 (尾号:7890) - 余额:¥10,000.00</option>
<option value="0987654321">活期账户 (尾号:4321) - 余额:¥5,000.00</option>
</select>
</div>
<div class="form-group">
<label for="to-account">转入账户:</label>
<select id="to-account" name="to-account">
<option value="5678901234">张三 (尾号:1234) - 工商银行</option>
<option value="6789012345">李四 (尾号:2345) - 建设银行</option>
<option value="7890123456">王五 (尾号:3456) - 农业银行</option>
</select>
</div>
<div class="form-group">
<label for="amount">转账金额:</label>
<input type="number" id="amount" name="amount" placeholder="请输入转账金额" min="0.01" step="0.01" required>
</div>
<div class="form-group">
<label for="transaction-password">交易密码:</label>
<input type="password" id="transaction-password" name="transaction-password" placeholder="请输入交易密码" required>
</div>
<button type="button" id="transfer-btn">确认转账</button>
</div>
<script>
// 显示消息
function showMessage(text, type) {
const message = document.getElementById('message');
message.textContent = text;
message.className = `message ${type}`;
message.style.display = 'block';
}
// 隐藏消息
function hideMessage() {
const message = document.getElementById('message');
message.style.display = 'none';
}
// 转账按钮点击事件
document.getElementById('transfer-btn').addEventListener('click', function() {
const amount = parseFloat(document.getElementById('amount').value);
const transactionPassword = document.getElementById('transaction-password').value;
// 简单验证
if (isNaN(amount) || amount <= 0) {
showMessage('请输入有效的转账金额', 'error');
return;
}
if (!transactionPassword) {
showMessage('请输入交易密码', 'error');
return;
}
if (transactionPassword !== '123456') {
showMessage('交易密码错误', 'error');
return;
}
// 显示加载中消息
showMessage('正在处理转账...', 'loading');
// 模拟网络延迟
setTimeout(() => {
hideMessage();
// 模拟转账成功
showMessage(`转账成功!转账金额:¥${amount.toFixed(2)}`, 'success');
// 清空表单
document.getElementById('amount').value = '';
document.getElementById('transaction-password').value = '';
}, 2000);
});
</script>
</body>
</html>
6.3 测试脚本实现
创建 test_bank_transfer.py 文件:
python
import time
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
# 创建 Edge 浏览器实例
driver = webdriver.Edge()
# 浏览器最大化显示
driver.maximize_window()
try:
# 访问页面
driver.get("http://localhost:8000/bank_transfer/bank_transfer.html")
# 选择转入账户为李四
to_account = driver.find_element(By.ID, "to-account")
to_account.send_keys("李四")
# 输入转账金额1000
amount_input = driver.find_element(By.ID, "amount")
amount_input.send_keys("1000")
# 输入交易密码123456
transaction_password = driver.find_element(By.ID, "transaction-password")
transaction_password.send_keys("123456")
# 点击"确认转账"按钮
transfer_button = driver.find_element(By.ID, "transfer-btn")
transfer_button.click()
# 等待转账结果提示
success_message = WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "message"), "转账成功")
)
# 断言验证
actual_message = driver.find_element(By.ID, "message").text
expected_message = "转账成功!转账金额:¥1000.00"
assert actual_message == expected_message, f"期望消息为 {expected_message},但实际为 {actual_message}"
print(f"测试通过!转账结果:{actual_message}")
except Exception as e:
print(f"测试失败:{e}")
finally:
# 测试完成后等待15秒再关闭浏览器
time.sleep(15)
driver.quit()
第7章 在线教育课程报名测试
7.1 场景说明
用户访问在线教育平台,浏览课程,选择课程,填写报名信息,提交报名。
7.2 网页实现
创建 online_education.html 文件:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在线教育课程报名测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.course-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.course-item {
border: 1px solid #ddd;
padding: 20px;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s;
}
.course-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.course-item h3 {
margin-top: 0;
}
.course-price {
font-size: 20px;
font-weight: bold;
color: #e74c3c;
margin: 10px 0;
}
.course-details {
margin-top: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.course-details h2 {
margin-top: 0;
}
.course-info {
display: flex;
gap: 20px;
margin: 20px 0;
color: #666;
}
.enrollment-form {
margin-top: 30px;
padding: 20px;
background-color: #f0f8ff;
border-radius: 8px;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 15px;
}
.form-group {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 5px;
font-weight: bold;
}
input, select, button {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
.payment-options {
margin: 20px 0;
}
.payment-options h4 {
margin-bottom: 10px;
}
.payment-options label {
display: inline;
font-weight: normal;
margin-right: 15px;
}
.message {
margin: 20px 0;
padding: 10px;
border-radius: 4px;
font-weight: bold;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.loading {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
#message {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>在线教育课程报名测试</h1>
<div id="message" class="message" style="display: none;"></div>
<h2>课程列表</h2>
<div class="course-list">
<div class="course-item" data-course="python">
<h3>Python编程基础</h3>
<p>适合零基础学员,学习Python编程的基础知识和核心概念。</p>
<div class="course-price">¥999.00</div>
<p>课时:40小时 | 讲师:张老师 | 评分:4.8/5.0</p>
</div>
<div class="course-item" data-course="java">
<h3>Java企业开发</h3>
<p>学习Java企业级应用开发,包括Spring Boot、Spring Cloud等框架。</p>
<div class="course-price">¥1299.00</div>
<p>课时:50小时 | 讲师:李老师 | 评分:4.7/5.0</p>
</div>
<div class="course-item" data-course="web">
<h3>Web前端开发</h3>
<p>学习HTML、CSS、JavaScript等Web前端技术,构建现代Web应用。</p>
<div class="course-price">¥1199.00</div>
<p>课时:45小时 | 讲师:王老师 | 评分:4.9/5.0</p>
</div>
</div>
<div id="course-details" class="course-details" style="display: none;">
<h2 id="course-title">Python编程基础</h2>
<div class="course-info">
<div>课时:<span id="course-hours">40小时</span></div>
<div>讲师:<span id="course-teacher">张老师</span></div>
<div>评分:<span id="course-rating">4.8/5.0</span></div>
</div>
<div class="course-price" id="course-price">¥999.00</div>
<div class="enrollment-form">
<h3>课程报名</h3>
<form id="enrollment-form">
<div class="form-row">
<div class="form-group">
<label for="student-name">姓名:</label>
<input type="text" id="student-name" name="student-name" placeholder="请输入姓名" required>
</div>
<div class="form-group">
<label for="student-phone">电话:</label>
<input type="tel" id="student-phone" name="student-phone" placeholder="请输入电话" required>
</div>
<div class="form-group">
<label for="student-email">邮箱:</label>
<input type="email" id="student-email" name="student-email" placeholder="请输入邮箱" required>
</div>
</div>
<div class="payment-options">
<h4>支付方式:</h4>
<label>
<input type="radio" name="payment-method" value="alipay" checked> 支付宝
</label>
<label>
<input type="radio" name="payment-method" value="wechat"> 微信支付
</label>
<label>
<input type="radio" name="payment-method" value="card"> 银行卡
</label>
</div>
<button type="submit">提交报名</button>
</form>
</div>
</div>
<script>
// 课程数据
const courseData = {
python: {
title: 'Python编程基础',
hours: '40小时',
teacher: '张老师',
rating: '4.8/5.0',
price: '¥999.00'
},
java: {
title: 'Java企业开发',
hours: '50小时',
teacher: '李老师',
rating: '4.7/5.0',
price: '¥1299.00'
},
web: {
title: 'Web前端开发',
hours: '45小时',
teacher: '王老师',
rating: '4.9/5.0',
price: '¥1199.00'
}
};
// 显示消息
function showMessage(text, type) {
const message = document.getElementById('message');
message.textContent = text;
message.className = `message ${type}`;
message.style.display = 'block';
}
// 隐藏消息
function hideMessage() {
const message = document.getElementById('message');
message.style.display = 'none';
}
// 选择课程
document.querySelectorAll('.course-item').forEach(item => {
item.addEventListener('click', function() {
const courseKey = this.dataset.course;
const course = courseData[courseKey];
// 更新课程详情
document.getElementById('course-title').textContent = course.title;
document.getElementById('course-hours').textContent = course.hours;
document.getElementById('course-teacher').textContent = course.teacher;
document.getElementById('course-rating').textContent = course.rating;
document.getElementById('course-price').textContent = course.price;
// 显示课程详情
document.getElementById('course-details').style.display = 'block';
});
});
// 提交报名
document.getElementById('enrollment-form').addEventListener('submit', function(e) {
e.preventDefault();
const studentName = document.getElementById('student-name').value;
const studentPhone = document.getElementById('student-phone').value;
const studentEmail = document.getElementById('student-email').value;
// 简单验证
if (!studentName || !studentPhone || !studentEmail) {
showMessage('请填写所有必填字段', 'error');
return;
}
// 显示加载中消息
showMessage('正在处理报名...', 'loading');
// 模拟网络延迟
setTimeout(() => {
hideMessage();
showMessage('报名成功!我们将尽快与您联系。', 'success');
// 清空表单
this.reset();
}, 1500);
});
</script>
</body>
</html>
7.3 测试脚本实现
创建 test_online_education.py 文件:
python
import time
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
# 创建 Edge 浏览器实例
driver = webdriver.Edge()
# 浏览器最大化显示
driver.maximize_window()
try:
# 访问页面
driver.get("http://localhost:8000/online_education/online_education.html")
# 点击"Python编程基础"课程
python_course = driver.find_element(By.XPATH, "//h3[text()='Python编程基础']/ancestor::div[@class='course-item']")
python_course.click()
# 等待课程详情显示
course_details = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "course-details"))
)
# 输入姓名
student_name = driver.find_element(By.ID, "student-name")
student_name.send_keys("张三")
# 输入电话
student_phone = driver.find_element(By.ID, "student-phone")
student_phone.send_keys("13800138000")
# 输入邮箱
student_email = driver.find_element(By.ID, "student-email")
student_email.send_keys("zhangsan@example.com")
# 选择微信支付
wechat_payment = driver.find_element(By.XPATH, "//input[@value='wechat']")
wechat_payment.click()
# 点击"提交报名"按钮
submit_button = driver.find_element(By.XPATH, "//button[text()='提交报名']")
submit_button.click()
# 等待报名成功提示
success_message = WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "message"), "报名成功")
)
# 断言验证
actual_message = driver.find_element(By.ID, "message").text
expected_message = "报名成功!我们将尽快与您联系。"
assert actual_message == expected_message, f"期望消息为 {expected_message},但实际为 {actual_message}"
print(f"测试通过!报名结果:{actual_message}")
except Exception as e:
print(f"测试失败:{e}")
finally:
# 测试完成后等待15秒再关闭浏览器
time.sleep(15)
driver.quit()
第8章 Selenium 自动化测试最佳实践
8.1 代码组织与结构
- 模块化设计:将测试代码按照功能或业务模块进行拆分,提高代码复用性和可维护性
- 页面对象模型(POM):将页面元素和操作封装为页面对象,减少代码重复,提高测试的可维护性
- 测试数据分离:将测试数据与测试代码分离,便于测试数据的管理和维护
8.2 等待策略
- 显式等待 :优先使用
WebDriverWait结合expected_conditions等待元素,避免硬编码等待时间 - 隐式等待:设置适当的隐式等待时间,作为备选方案
- 避免使用 Thread.sleep():除非特殊情况,否则不建议使用硬编码的等待时间
8.3 元素定位策略
- 优先使用 ID:ID 是最稳定的定位方式,优先使用
- 其次使用 XPath:XPath 定位灵活,但要注意避免使用过于复杂或不稳定的 XPath
- CSS 选择器:对于复杂的元素定位,可以使用 CSS 选择器
- 避免使用链接文本:链接文本可能会频繁变化,影响测试的稳定性
8.4 测试用例设计
- 单一职责原则:每个测试用例只测试一个功能点
- 测试用例独立:测试用例之间应该相互独立,不依赖于其他测试用例的执行结果
- 正向测试与反向测试结合:既要测试正常流程,也要测试异常情况
- 测试数据多样化:使用不同的测试数据覆盖各种场景
8.5 错误处理与日志
- 捕获异常:使用 try-except 块捕获并处理异常,避免测试因单个错误而完全失败
- 添加日志:在关键步骤添加日志,便于调试和分析测试结果
- 截图功能:在测试失败时自动截图,便于问题定位
8.6 测试执行与报告
- 批量执行:使用测试框架(如 pytest)批量执行测试用例
- 生成报告:生成详细的测试报告,包括测试结果、执行时间、失败原因等
- 持续集成:将自动化测试集成到 CI/CD 流程中,实现持续测试
8.7 性能与稳定性
- 避免不必要的操作:减少测试中的不必要操作,提高测试执行效率
- 合理设置等待时间:根据页面加载情况设置合理的等待时间,避免测试不稳定
- 资源清理:在测试完成后清理资源,如关闭浏览器、删除临时文件等
第9章 结论
通过本教程,我们学习了如何使用 Selenium 进行自动化测试,包括:
- 环境搭建:创建虚拟环境,安装依赖,配置浏览器驱动
- 基础操作:元素定位、点击、输入、等待等基本操作
- 测试场景 :
- 运费计算功能测试
- 在线注册功能测试
- 电商购物车测试
- 航班查询功能测试
- 银行转账功能测试
- 在线教育课程报名测试
- 最佳实践:代码组织、等待策略、元素定位、测试用例设计等
自动化测试可以提高测试效率,减少人工测试的工作量,确保软件质量。在实际项目中,我们需要根据项目特点和需求,选择合适的测试框架和工具,设计合理的测试用例,编写高质量的测试代码。
希望本教程对你有所帮助,祝你在自动化测试的道路上越走越远!
附录:常见问题与解决方案
Q1:浏览器驱动无法下载
解决方案:
- 检查网络连接是否正常
- 手动下载对应版本的浏览器驱动,放在系统 PATH 目录下
- 使用系统已安装的浏览器驱动
Q2:元素定位失败
解决方案:
- 检查元素定位表达式是否正确
- 确认元素是否在 iframe 中
- 确认元素是否为动态生成的
- 使用显式等待等待元素加载完成
Q3:测试执行不稳定
解决方案:
- 增加合理的等待时间
- 避免使用 Thread.sleep(),使用显式等待
- 确保测试用例之间相互独立
- 减少测试中的不必要操作
Q4:浏览器无法打开
解决方案:
- 检查浏览器是否已安装
- 确认浏览器驱动版本与浏览器版本匹配
- 检查系统 PATH 是否包含浏览器驱动路径
Q5:测试报告不美观
解决方案:
- 使用 pytest-html 等插件生成美观的 HTML 报告
- 配置报告样式,添加必要的信息
- 集成到持续集成工具中,如 Jenkins、GitLab CI 等