测试脚本编程题(5题)
!!! tip "编程提示" - 认真分析题目要求,理解测试场景 - 注意代码的规范性和可读性 - 包含必要的异常处理和日志记录 - 考虑实际项目中的最佳实践
题目1:基础登录自动化测试(20分)
题目要求: 编写一个完整的登录页面自动化测试脚本,要求包含以下功能: 1. 使用Page Object模式设计 2. 测试有效登录和无效登录场景 3. 使用pytest框架和参数化测试 4. 包含适当的等待和异常处理 5. 集成日志记录功能
技术要求:
- 使用webdriver-manager管理驱动
- 显式等待处理动态元素
- 测试数据外部化(使用参数化)
🔍 点击查看参考答案
Python
# conftest.py
import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
import logging
@pytest.fixture(scope="session")
def setup_logging():
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('test_execution.log'),
logging.StreamHandler()
]
)
return logging.getLogger(__name__)
@pytest.fixture(scope="function")
def driver():
# 使用webdriver-manager自动管理驱动
service = Service(ChromeDriverManager().install())
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 无头模式,适合CI/CD
driver = webdriver.Chrome(service=service, options=options)
driver.implicitly_wait(10)
driver.maximize_window()
yield driver
driver.quit()
# page_objects.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import logging
class LoginPageElements:
"""对象库层:定义页面元素"""
USERNAME_INPUT = (By.ID, "username")
PASSWORD_INPUT = (By.ID, "password")
LOGIN_BUTTON = (By.ID, "loginBtn")
ERROR_MESSAGE = (By.CLASS_NAME, "error-message")
SUCCESS_MESSAGE = (By.CLASS_NAME, "welcome-message")
LOGOUT_BUTTON = (By.ID, "logoutBtn")
class LoginPageActions:
"""操作层:封装页面操作"""
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
self.logger = logging.getLogger(__name__)
def navigate_to_login_page(self, url="https://example.com/login"):
"""导航到登录页面"""
self.logger.info(f"导航到登录页面: {url}")
self.driver.get(url)
def enter_username(self, username):
"""输入用户名"""
self.logger.info(f"输入用户名: {username}")
username_field = self.wait.until(
EC.presence_of_element_located(LoginPageElements.USERNAME_INPUT)
)
username_field.clear()
username_field.send_keys(username)
def enter_password(self, password):
"""输入密码"""
self.logger.info("输入密码")
password_field = self.wait.until(
EC.presence_of_element_located(LoginPageElements.PASSWORD_INPUT)
)
password_field.clear()
password_field.send_keys(password)
def click_login_button(self):
"""点击登录按钮"""
self.logger.info("点击登录按钮")
login_button = self.wait.until(
EC.element_to_be_clickable(LoginPageElements.LOGIN_BUTTON)
)
login_button.click()
def get_error_message(self):
"""获取错误信息"""
try:
error_element = self.wait.until(
EC.presence_of_element_located(LoginPageElements.ERROR_MESSAGE)
)
return error_element.text
except:
return None
def is_login_successful(self):
"""检查登录是否成功"""
try:
self.wait.until(
EC.presence_of_element_located(LoginPageElements.SUCCESS_MESSAGE)
)
return True
except:
return False
def login(self, username, password):
"""完整的登录操作"""
self.logger.info(f"执行登录操作 - 用户名: {username}")
self.enter_username(username)
self.enter_password(password)
self.click_login_button()
# test_login.py
import pytest
from page_objects import LoginPageActions
import logging
class TestLogin:
"""业务层:测试用例"""
@pytest.fixture(autouse=True)
def setup(self, driver, setup_logging):
self.driver = driver
self.login_page = LoginPageActions(driver)
self.logger = logging.getLogger(__name__)
@pytest.mark.parametrize("username,password,expected_result", [
("admin", "admin123", "success"),
("user", "user123", "success"),
("admin", "wrongpass", "error"),
("", "admin123", "error"),
("admin", "", "error"),
("nonexistent", "123456", "error")
])
def test_login_scenarios(self, username, password, expected_result):
"""参数化测试不同登录场景"""
self.logger.info(f"=== 开始测试登录场景: {username}/{password} ===")
# 导航到登录页面
self.login_page.navigate_to_login_page()
# 执行登录操作
self.login_page.login(username, password)
# 验证结果
if expected_result == "success":
assert self.login_page.is_login_successful(), f"用户 {username} 登录应该成功"
self.logger.info("登录成功验证通过")
else:
error_msg = self.login_page.get_error_message()
assert error_msg is not None, "应该显示错误信息"
self.logger.info(f"登录失败验证通过,错误信息: {error_msg}")
def test_empty_credentials(self):
"""测试空凭据场景"""
self.logger.info("=== 测试空凭据场景 ===")
self.login_page.navigate_to_login_page()
self.login_page.click_login_button() # 直接点击登录,不输入任何信息
error_msg = self.login_page.get_error_message()
assert error_msg is not None, "空凭据应该显示错误信息"
self.logger.info(f"空凭据测试通过: {error_msg}")
if __name__ == "__main__":
pytest.main(["-v", "--html=login_test_report.html", "--self-contained-html"])
评分要点:
- Page Object模式实现(5分)
- 参数化测试使用(3分)
- 等待机制处理(4分)
- 异常处理(3分)
- 日志记录(3分)
- 代码规范性(2分)
题目2:多窗口操作自动化测试(15分)
题目要求: 编写一个处理多窗口操作的自动化测试脚本: 1. 在主窗口点击链接打开新窗口 2. 切换到新窗口进行操作 3. 关闭新窗口并返回主窗口 4. 验证窗口切换的正确性 5. 包含完整的窗口句柄管理
🔍 点击查看参考答案
Python
import pytest
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 logging
class WindowManager:
"""窗口管理类"""
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
self.logger = logging.getLogger(__name__)
self.main_window = None
def save_main_window(self):
"""保存主窗口句柄"""
self.main_window = self.driver.current_window_handle
self.logger.info(f"保存主窗口句柄: {self.main_window}")
def open_new_window(self, link_locator):
"""点击链接打开新窗口"""
current_windows = len(self.driver.window_handles)
self.logger.info(f"当前窗口数量: {current_windows}")
# 点击会打开新窗口的链接
link = self.wait.until(EC.element_to_be_clickable(link_locator))
link.click()
# 等待新窗口打开
self.wait.until(EC.number_of_windows_to_be(current_windows + 1))
self.logger.info("新窗口已打开")
def switch_to_new_window(self):
"""切换到新窗口"""
all_windows = self.driver.window_handles
for window in all_windows:
if window != self.main_window:
self.driver.switch_to.window(window)
self.logger.info(f"切换到新窗口: {window}")
return window
raise Exception("未找到新窗口")
def switch_to_main_window(self):
"""切换回主窗口"""
self.driver.switch_to.window(self.main_window)
self.logger.info("切换回主窗口")
def close_current_window(self):
"""关闭当前窗口"""
current_window = self.driver.current_window_handle
self.driver.close()
self.logger.info(f"关闭窗口: {current_window}")
def get_current_window_title(self):
"""获取当前窗口标题"""
return self.driver.title
class TestMultiWindow:
"""多窗口测试类"""
@pytest.fixture(autouse=True)
def setup(self, driver):
self.driver = driver
self.window_manager = WindowManager(driver)
self.logger = logging.getLogger(__name__)
def test_multi_window_operations(self):
"""测试多窗口操作流程"""
self.logger.info("=== 开始多窗口操作测试 ===")
# 1. 导航到主页面
self.driver.get("https://example.com/main")
self.window_manager.save_main_window()
main_title = self.window_manager.get_current_window_title()
self.logger.info(f"主窗口标题: {main_title}")
# 2. 打开新窗口
new_window_link = (By.ID, "openNewWindow")
self.window_manager.open_new_window(new_window_link)
# 3. 切换到新窗口
new_window_handle = self.window_manager.switch_to_new_window()
new_window_title = self.window_manager.get_current_window_title()
self.logger.info(f"新窗口标题: {new_window_title}")
# 4. 在新窗口中执行操作
self.perform_operations_in_new_window()
# 5. 关闭新窗口
self.window_manager.close_current_window()
# 6. 切换回主窗口
self.window_manager.switch_to_main_window()
current_title = self.window_manager.get_current_window_title()
# 7. 验证回到主窗口
assert current_title == main_title, "未正确返回主窗口"
assert len(self.driver.window_handles) == 1, "应该只剩一个窗口"
self.logger.info("多窗口操作测试完成")
def perform_operations_in_new_window(self):
"""在新窗口中执行具体操作"""
self.logger.info("在新窗口中执行操作")
# 示例:在新窗口中填写表单
try:
name_input = WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.ID, "name"))
)
name_input.send_keys("测试用户")
email_input = self.driver.find_element(By.ID, "email")
email_input.send_keys("test@example.com")
submit_btn = self.driver.find_element(By.ID, "submit")
submit_btn.click()
self.logger.info("新窗口操作完成")
except Exception as e:
self.logger.warning(f"新窗口操作失败: {str(e)}")
def test_multiple_new_windows(self):
"""测试打开多个新窗口的场景"""
self.logger.info("=== 测试多个新窗口场景 ===")
self.driver.get("https://example.com/main")
self.window_manager.save_main_window()
# 打开两个新窗口
for i in range(2):
link_locator = (By.CSS_SELECTOR, f".open-window-{i+1}")
self.window_manager.open_new_window(link_locator)
# 验证窗口数量
assert len(self.driver.window_handles) == 3, "应该有3个窗口"
# 逐个处理新窗口
all_windows = self.driver.window_handles
for window in all_windows:
if window != self.window_manager.main_window:
self.driver.switch_to.window(window)
title = self.driver.title
self.logger.info(f"处理窗口: {title}")
self.driver.close()
# 返回主窗口
self.window_manager.switch_to_main_window()
assert len(self.driver.window_handles) == 1, "最终应该只剩主窗口"
评分要点:
- 窗口句柄管理(4分)
- 窗口切换逻辑(4分)
- 等待机制(3分)
- 异常处理(2分)
- 代码组织性(2分)
题目3:数据驱动测试实现(15分)
题目要求: 设计一个数据驱动的搜索功能测试: 1. 从Excel文件读取测试数据 2. 使用不同搜索关键词进行测试 3. 验证搜索结果的正确性 4. 生成详细的测试报告 5. 处理数据文件异常情况
🔍 点击查看参考答案
Python
# data_manager.py
import pandas as pd
import logging
from typing import List, Dict
class TestDataManager:
"""测试数据管理类"""
def __init__(self, data_file_path: str):
self.data_file_path = data_file_path
self.logger = logging.getLogger(__name__)
def load_test_data(self) -> List[Dict]:
"""从Excel文件加载测试数据"""
try:
self.logger.info(f"加载测试数据文件: {self.data_file_path}")
df = pd.read_excel(self.data_file_path)
# 转换为字典列表
test_data = df.to_dict('records')
self.logger.info(f"成功加载 {len(test_data)} 条测试数据")
return test_data
except FileNotFoundError:
self.logger.error(f"测试数据文件未找到: {self.data_file_path}")
raise
except Exception as e:
self.logger.error(f"加载测试数据失败: {str(e)}")
raise
def validate_data_structure(self, data: List[Dict]) -> bool:
"""验证数据结构"""
required_fields = ['search_term', 'expected_results', 'min_results']
for i, record in enumerate(data):
for field in required_fields:
if field not in record:
self.logger.error(f"第{i+1}行数据缺少必要字段: {field}")
return False
self.logger.info("数据结构验证通过")
return True
# search_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
import logging
class SearchPageElements:
SEARCH_INPUT = (By.ID, "searchInput")
SEARCH_BUTTON = (By.ID, "searchBtn")
SEARCH_RESULTS = (By.CLASS_NAME, "search-result")
RESULT_TITLE = (By.CLASS_NAME, "result-title")
NO_RESULTS_MESSAGE = (By.CLASS_NAME, "no-results")
class SearchPageActions:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
self.logger = logging.getLogger(__name__)
def navigate_to_search_page(self, url="https://example.com/search"):
"""导航到搜索页面"""
self.logger.info(f"导航到搜索页面: {url}")
self.driver.get(url)
def perform_search(self, search_term: str):
"""执行搜索"""
self.logger.info(f"搜索关键词: {search_term}")
# 清空并输入搜索词
search_input = self.wait.until(
EC.presence_of_element_located(SearchPageElements.SEARCH_INPUT)
)
search_input.clear()
search_input.send_keys(search_term)
# 执行搜索
search_input.send_keys(Keys.ENTER)
# 或者点击搜索按钮
# search_button = self.driver.find_element(*SearchPageElements.SEARCH_BUTTON)
# search_button.click()
def get_search_results(self) -> List[str]:
"""获取搜索结果"""
try:
# 等待搜索结果加载
self.wait.until(
EC.presence_of_element_located(SearchPageElements.SEARCH_RESULTS)
)
results = self.driver.find_elements(*SearchPageElements.SEARCH_RESULTS)
result_texts = []
for result in results:
try:
title_element = result.find_element(*SearchPageElements.RESULT_TITLE)
result_texts.append(title_element.text)
except:
result_texts.append(result.text)
self.logger.info(f"找到 {len(result_texts)} 个搜索结果")
return result_texts
except:
# 检查是否有"无结果"消息
try:
self.driver.find_element(*SearchPageElements.NO_RESULTS_MESSAGE)
self.logger.info("搜索无结果")
return []
except:
self.logger.warning("搜索结果页面加载异常")
return []
def verify_search_results(self, results: List[str], search_term: str) -> bool:
"""验证搜索结果是否包含搜索词"""
if not results:
return False
relevant_count = 0
for result in results:
if search_term.lower() in result.lower():
relevant_count += 1
relevance_ratio = relevant_count / len(results)
self.logger.info(f"搜索结果相关性: {relevance_ratio:.2%}")
return relevance_ratio >= 0.7 # 70%的结果应该相关
# test_search_data_driven.py
import pytest
from data_manager import TestDataManager
from search_page import SearchPageActions
import logging
class TestSearchDataDriven:
"""数据驱动搜索测试"""
@pytest.fixture(scope="class")
def test_data(self):
"""加载测试数据"""
data_manager = TestDataManager("test_data/search_test_data.xlsx")
data = data_manager.load_test_data()
assert data_manager.validate_data_structure(data), "测试数据结构验证失败"
return data
@pytest.fixture(autouse=True)
def setup(self, driver):
self.driver = driver
self.search_page = SearchPageActions(driver)
self.logger = logging.getLogger(__name__)
def test_search_with_data(self, test_data):
"""使用测试数据进行搜索测试"""
test_results = []
for i, data_row in enumerate(test_data):
self.logger.info(f"=== 执行第 {i+1} 个搜索测试 ===")
search_term = data_row['search_term']
expected_results = data_row['expected_results']
min_results = int(data_row['min_results'])
try:
# 执行搜索
self.search_page.navigate_to_search_page()
self.search_page.perform_search(search_term)
results = self.search_page.get_search_results()
# 验证结果数量
results_count_valid = len(results) >= min_results
# 验证结果相关性
relevance_valid = self.search_page.verify_search_results(results, search_term)
# 记录测试结果
test_result = {
'search_term': search_term,
'expected_results': expected_results,
'actual_results': len(results),
'min_results': min_results,
'results_count_valid': results_count_valid,
'relevance_valid': relevance_valid,
'overall_pass': results_count_valid and relevance_valid
}
test_results.append(test_result)
# 断言
assert results_count_valid, f"搜索'{search_term}'结果数量不足,期望至少{min_results}个,实际{len(results)}个"
assert relevance_valid, f"搜索'{search_term}'结果相关性不足"
self.logger.info(f"搜索测试通过: {search_term}")
except Exception as e:
self.logger.error(f"搜索测试失败: {search_term}, 错误: {str(e)}")
test_results.append({
'search_term': search_term,
'error': str(e),
'overall_pass': False
})
raise
# 生成测试报告摘要
self.generate_test_summary(test_results)
def generate_test_summary(self, test_results):
"""生成测试摘要"""
total_tests = len(test_results)
passed_tests = sum(1 for result in test_results if result.get('overall_pass', False))
self.logger.info(f"=== 搜索测试摘要 ===")
self.logger.info(f"总测试数: {total_tests}")
self.logger.info(f"通过数: {passed_tests}")
self.logger.info(f"失败数: {total_tests - passed_tests}")
self.logger.info(f"通过率: {passed_tests/total_tests:.2%}")
# 创建测试数据文件 (search_test_data.xlsx)
# 示例数据结构:
"""
search_term | expected_results | min_results
Python | 编程语言相关 | 5
Selenium | 自动化测试相关 | 3
机器学习 | AI相关 | 4
"""
评分要点:
- 数据文件读取(3分)
- 数据结构验证(3分)
- 搜索功能测试(4分)
- 结果验证逻辑(3分)
- 异常处理(2分)
题目4:完整的E2E测试流程(25分)
题目要求: 设计一个完整的电商网站端到端测试流程: 1. 用户注册/登录 2. 商品搜索和浏览 3. 添加商品到购物车 4. 结算和支付流程 5. 订单确认 6. 全流程使用PO模式和数据驱动 7. 包含详细的测试报告和截图
🔍 点击查看参考答案
由于篇幅限制,这里提供核心框架代码:
Python
# e2e_test_framework.py
import pytest
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import logging
from datetime import datetime
import os
class E2ETestFramework:
"""E2E测试框架基类"""
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 15)
self.logger = logging.getLogger(__name__)
self.screenshots_dir = "screenshots"
os.makedirs(self.screenshots_dir, exist_ok=True)
def take_screenshot(self, step_name):
"""截图功能"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{step_name}_{timestamp}.png"
filepath = os.path.join(self.screenshots_dir, filename)
self.driver.save_screenshot(filepath)
self.logger.info(f"截图保存: {filepath}")
return filepath
class UserAccountPage(E2ETestFramework):
"""用户账户页面"""
def register_user(self, user_data):
"""用户注册"""
self.logger.info("开始用户注册")
# 注册逻辑实现
self.take_screenshot("user_registration")
def login_user(self, username, password):
"""用户登录"""
self.logger.info(f"用户登录: {username}")
# 登录逻辑实现
self.take_screenshot("user_login")
class ProductPage(E2ETestFramework):
"""商品页面"""
def search_product(self, keyword):
"""搜索商品"""
self.logger.info(f"搜索商品: {keyword}")
# 搜索逻辑实现
self.take_screenshot("product_search")
def add_to_cart(self, product_id):
"""添加到购物车"""
self.logger.info(f"添加商品到购物车: {product_id}")
# 添加购物车逻辑
self.take_screenshot("add_to_cart")
class CheckoutPage(E2ETestFramework):
"""结算页面"""
def proceed_to_checkout(self):
"""进入结算流程"""
self.logger.info("开始结算流程")
self.take_screenshot("checkout_start")
def complete_payment(self, payment_info):
"""完成支付"""
self.logger.info("执行支付操作")
self.take_screenshot("payment_complete")
class TestE2EWorkflow:
"""完整E2E测试流程"""
@pytest.fixture(autouse=True)
def setup(self, driver):
self.driver = driver
self.user_page = UserAccountPage(driver)
self.product_page = ProductPage(driver)
self.checkout_page = CheckoutPage(driver)
self.logger = logging.getLogger(__name__)
def test_complete_purchase_workflow(self):
"""完整的购买流程测试"""
self.logger.info("=== 开始E2E购买流程测试 ===")
try:
# 1. 用户注册/登录
self.user_page.login_user("testuser", "password123")
# 2. 搜索商品
self.product_page.search_product("手机")
# 3. 添加商品到购物车
self.product_page.add_to_cart("product_001")
# 4. 结算流程
self.checkout_page.proceed_to_checkout()
# 5. 支付
payment_info = {"card": "1234567890123456", "cvv": "123"}
self.checkout_page.complete_payment(payment_info)
# 6. 验证订单
self.verify_order_completion()
self.logger.info("E2E测试流程完成")
except Exception as e:
self.logger.error(f"E2E测试失败: {str(e)}")
self.user_page.take_screenshot("test_failure")
raise
def verify_order_completion(self):
"""验证订单完成"""
# 订单验证逻辑
self.logger.info("订单完成验证通过")
评分要点:
- 完整流程设计(8分)
- PO模式应用(5分)
- 截图功能(3分)
- 异常处理(4分)
- 测试数据管理(3分)
- 代码组织性(2分)
题目5:性能监控与报告生成(20分)
题目要求: 开发一个自动化测试性能监控和报告生成系统: 1. 监控页面加载时间 2. 记录操作响应时间 3. 检测页面性能指标 4. 生成HTML格式的详细报告 5. 集成邮件通知功能
🔍 点击查看参考答案
Python
# performance_monitor.py
import time
import json
from datetime import datetime
import logging
from typing import Dict, List
import smtplib
from email.mime.text import MimeText
from email.mime.multipart import MimeMultipart
class PerformanceMonitor:
"""性能监控类"""
def __init__(self, driver):
self.driver = driver
self.logger = logging.getLogger(__name__)
self.performance_data = []
self.start_time = None
def start_timing(self, operation_name: str):
"""开始计时"""
self.start_time = time.time()
self.logger.info(f"开始监控操作: {operation_name}")
def end_timing(self, operation_name: str) -> float:
"""结束计时并记录"""
if self.start_time is None:
raise ValueError("必须先调用start_timing()")
end_time = time.time()
duration = end_time - self.start_time
performance_record = {
'operation': operation_name,
'duration': duration,
'timestamp': datetime.now().isoformat(),
'url': self.driver.current_url,
'page_title': self.driver.title
}
self.performance_data.append(performance_record)
self.logger.info(f"操作 '{operation_name}' 耗时: {duration:.2f}秒")
return duration
def get_page_load_time(self) -> float:
"""获取页面加载时间"""
navigation_start = self.driver.execute_script(
"return window.performance.timing.navigationStart"
)
load_event_end = self.driver.execute_script(
"return window.performance.timing.loadEventEnd"
)
if load_event_end == 0:
return -1 # 页面还在加载
load_time = (load_event_end - navigation_start) / 1000.0
self.logger.info(f"页面加载时间: {load_time:.2f}秒")
return load_time
def get_page_performance_metrics(self) -> Dict:
"""获取页面性能指标"""
try:
metrics = self.driver.execute_script("""
var timing = window.performance.timing;
var navigation = window.performance.getEntriesByType('navigation')[0];
return {
'dns_lookup': timing.domainLookupEnd - timing.domainLookupStart,
'tcp_connect': timing.connectEnd - timing.connectStart,
'request_response': timing.responseEnd - timing.requestStart,
'dom_processing': timing.domComplete - timing.domLoading,
'page_load_time': timing.loadEventEnd - timing.navigationStart,
'first_paint': navigation ? navigation.loadEventEnd - navigation.fetchStart : null
};
""")
# 转换为秒
for key in metrics:
if metrics[key] and metrics[key] > 0:
metrics[key] = metrics[key] / 1000.0
return metrics
except Exception as e:
self.logger.error(f"获取性能指标失败: {str(e)}")
return {}
class ReportGenerator:
"""报告生成器"""
def __init__(self, performance_data: List[Dict]):
self.performance_data = performance_data
self.logger = logging.getLogger(__name__)
def generate_html_report(self, output_file: str = "performance_report.html"):
"""生成HTML报告"""
html_template = """
<!DOCTYPE html>
<html>
<head>
<title>自动化测试性能报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #f0f0f0; padding: 10px; text-align: center; }
.summary { margin: 20px 0; padding: 15px; border: 1px solid #ddd; }
.data-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
.data-table th, .data-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
.data-table th { background-color: #f2f2f2; }
.slow { background-color: #ffebee; }
.normal { background-color: #e8f5e8; }
.chart { margin: 20px 0; text-align: center; }
</style>
</head>
<body>
<div class="header">
<h1>自动化测试性能报告</h1>
<p>生成时间: {report_time}</p>
</div>
<div class="summary">
<h2>性能摘要</h2>
<p>总操作数: {total_operations}</p>
<p>平均响应时间: {avg_response_time:.2f}秒</p>
<p>最慢操作: {slowest_operation}</p>
<p>最快操作: {fastest_operation}</p>
</div>
<div class="details">
<h2>详细数据</h2>
<table class="data-table">
<thead>
<tr>
<th>操作名称</th>
<th>耗时(秒)</th>
<th>时间戳</th>
<th>页面URL</th>
<th>页面标题</th>
</tr>
</thead>
<tbody>
{table_rows}
</tbody>
</table>
</div>
</body>
</html>
"""
# 计算统计数据
if not self.performance_data:
self.logger.warning("没有性能数据可生成报告")
return
durations = [item['duration'] for item in self.performance_data]
avg_duration = sum(durations) / len(durations)
slowest = max(self.performance_data, key=lambda x: x['duration'])
fastest = min(self.performance_data, key=lambda x: x['duration'])
# 生成表格行
table_rows = ""
for item in self.performance_data:
row_class = "slow" if item['duration'] > avg_duration * 1.5 else "normal"
table_rows += f"""
<tr class="{row_class}">
<td>{item['operation']}</td>
<td>{item['duration']:.2f}</td>
<td>{item['timestamp']}</td>
<td>{item['url']}</td>
<td>{item['page_title']}</td>
</tr>
"""
# 填充模板
html_content = html_template.format(
report_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
total_operations=len(self.performance_data),
avg_response_time=avg_duration,
slowest_operation=f"{slowest['operation']} ({slowest['duration']:.2f}s)",
fastest_operation=f"{fastest['operation']} ({fastest['duration']:.2f}s)",
table_rows=table_rows
)
# 写入文件
with open(output_file, 'w', encoding='utf-8') as f:
f.write(html_content)
self.logger.info(f"性能报告已生成: {output_file}")
return output_file
class EmailNotifier:
"""邮件通知类"""
def __init__(self, smtp_server: str, smtp_port: int, username: str, password: str):
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.username = username
self.password = password
self.logger = logging.getLogger(__name__)
def send_report(self, report_file: str, recipients: List[str], subject: str = "自动化测试性能报告"):
"""发送报告邮件"""
try:
msg = MimeMultipart()
msg['From'] = self.username
msg['To'] = ", ".join(recipients)
msg['Subject'] = subject
# 邮件正文
body = f"""
自动化测试性能报告已生成,请查看附件。
报告生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
"""
msg.attach(MimeText(body, 'plain', 'utf-8'))
# 添加附件
with open(report_file, 'r', encoding='utf-8') as f:
attachment = MimeText(f.read(), 'html', 'utf-8')
attachment.add_header('Content-Disposition', f'attachment; filename="{report_file}"')
msg.attach(attachment)
# 发送邮件
server = smtplib.SMTP(self.smtp_server, self.smtp_port)
server.starttls()
server.login(self.username, self.password)
server.send_message(msg)
server.quit()
self.logger.info(f"报告邮件已发送至: {recipients}")
except Exception as e:
self.logger.error(f"发送邮件失败: {str(e)}")
# 测试用例
class TestWithPerformanceMonitoring:
"""带性能监控的测试类"""
@pytest.fixture(autouse=True)
def setup(self, driver):
self.driver = driver
self.monitor = PerformanceMonitor(driver)
self.logger = logging.getLogger(__name__)
def test_performance_monitoring(self):
"""性能监控测试示例"""
# 监控页面加载
self.monitor.start_timing("页面加载")
self.driver.get("https://example.com")
self.monitor.end_timing("页面加载")
# 监控登录操作
self.monitor.start_timing("用户登录")
# 执行登录操作
time.sleep(2) # 模拟操作时间
self.monitor.end_timing("用户登录")
# 监控搜索操作
self.monitor.start_timing("商品搜索")
# 执行搜索操作
time.sleep(1.5) # 模拟操作时间
self.monitor.end_timing("商品搜索")
# 生成报告
generator = ReportGenerator(self.monitor.performance_data)
report_file = generator.generate_html_report()
# 发送邮件通知(可选)
# notifier = EmailNotifier("smtp.gmail.com", 587, "user@gmail.com", "password")
# notifier.send_report(report_file, ["admin@company.com"])
assert len(self.monitor.performance_data) > 0, "应该记录性能数据"
评分要点:
- 性能数据收集(5分)
- 时间监控功能(4分)
- HTML报告生成(5分)
- 数据分析统计(3分)
- 邮件通知功能(3分)
!!! success "测试脚本编程题完成" 🎉 恭喜您完成了全部5道测试脚本编程题!
Text Only
**编程要点总结:**
- 熟练掌握Page Object模式的三层架构
- 能够处理复杂的异步操作和等待机制
- 具备数据驱动测试的设计和实现能力
- 掌握多窗口、文件操作等高级功能
- 能够设计完整的测试框架和报告系统