Files
novalon-website/e2e-tests/tests/test_performance.py
T
张翔 f5dec95a83 feat: 添加管理后台页面和功能,优化测试和性能配置
refactor: 重构页面导航和滚动逻辑,提升用户体验

test: 更新测试配置和用例,增加覆盖率和稳定性

perf: 优化性能指标和阈值,适应开发环境需求

ci: 添加Lighthouse CI工作流,集成性能测试

docs: 更新API文档和健康检查端点

fix: 修复登录页面和表单提交问题

style: 调整响应式布局和可访问性改进

chore: 更新依赖项和脚本配置
2026-03-24 10:11:30 +08:00

322 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
性能测试模块
测试网站性能指标
"""
import pytest
import time
from typing import Dict, Any
from datetime import datetime
from pages.home_page import HomePage
from pages.contact_page import ContactPage
from config.settings import get_settings
class TestPerformance:
"""性能测试类"""
@pytest.mark.performance
@pytest.mark.smoke
def test_home_page_load_time(self, home_page: HomePage):
"""测试首页加载时间"""
home_page.navigate()
start_time = time.time()
home_page.wait_for_load()
end_time = time.time()
load_time = (end_time - start_time) * 1000 # 毫秒
# 阈值:5秒
assert load_time < 5000, f"首页加载时间 {load_time:.2f}ms 超过5秒阈值"
@pytest.mark.performance
@pytest.mark.smoke
def test_contact_page_load_time(self, contact_page: ContactPage):
"""测试联系页面加载时间"""
contact_page.navigate()
start_time = time.time()
contact_page.wait_for_load()
end_time = time.time()
load_time = (end_time - start_time) * 1000
# 阈值:5秒
assert load_time < 5000, f"联系页面加载时间 {load_time:.2f}ms 超过5秒阈值"
@pytest.mark.performance
def test_dom_content_loaded_time(self, home_page: HomePage):
"""测试DOM内容加载时间"""
home_page.navigate()
performance_data = home_page.execute_js("""
() => {
const timing = performance.timing;
return {
domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart,
domInteractive: timing.domInteractive - timing.domLoading
};
}
""")
dom_loaded = performance_data.get("domContentLoaded", 0)
assert dom_loaded < 3000, f"DOM内容加载时间 {dom_loaded}ms 超过3秒阈值"
@pytest.mark.performance
def test_first_paint_time(self, home_page: HomePage):
"""测试首次绘制时间"""
home_page.navigate()
first_paint = home_page.execute_js("""
() => {
const entries = performance.getEntriesByType('paint');
const firstPaint = entries.find(e => e.name === 'first-paint');
return firstPaint ? firstPaint.startTime : 0;
}
""")
if first_paint:
assert first_paint < 2000, f"首次绘制时间 {first_paint:.2f}ms 超过2秒阈值"
@pytest.mark.performance
def test_first_contentful_paint(self, home_page: HomePage):
"""测试首次内容绘制(FCP"""
home_page.navigate()
fcp = home_page.execute_js("""
() => {
const navigation = performance.getEntriesByType('navigation')[0];
return navigation ? navigation.firstContentfulPaint : 0;
}
""")
if fcp:
# 阈值:1.5秒
assert fcp < 1500, f"首次内容绘制时间 {fcp:.2f}ms 超过1.5秒阈值"
@pytest.mark.performance
def test_largest_contentful_paint(self, home_page: HomePage):
"""测试最大内容绘制(LCP"""
home_page.navigate()
lcp = home_page.execute_js("""
() => {
try {
const navigation = performance.getEntriesByType('navigation')[0];
return navigation ? navigation.largestContentfulPaint : 0;
} catch (e) {
return 0;
}
}
""")
if lcp:
# 阈值:2.5秒
assert lcp < 2500, f"最大内容绘制时间 {lcp:.2f}ms 超过2.5秒阈值"
@pytest.mark.performance
def test_time_to_interactive(self, home_page: HomePage):
"""测试可交互时间(TTI"""
home_page.navigate()
tti = home_page.execute_js("""
() => {
try {
const navigation = performance.getEntriesByType('navigation')[0];
return navigation ? navigation.interactive : 0;
} catch (e) {
return 0;
}
}
""")
if tti:
# 阈值:3秒
assert tti < 3000, f"可交互时间 {tti:.2f}ms 超过3秒阈值"
@pytest.mark.performance
def test_page_load_performance_metrics(self, home_page: HomePage):
"""测试页面加载性能指标"""
home_page.navigate()
performance = home_page.verify_page_performance()
# 验证关键指标
if performance.get("pageLoadTime"):
assert performance["pageLoadTime"] < 5000
if performance.get("domContentLoaded"):
assert performance["domContentLoaded"] < 3000
@pytest.mark.performance
def test_network_timing(self, home_page: HomePage):
"""测试网络时序"""
home_page.navigate()
timing = home_page.execute_js("""
() => {
const timing = performance.timing;
return {
dnsLookup: timing.domainLookupEnd - timing.domainLookupStart,
tcpConnection: timing.connectEnd - timing.connectStart,
serverResponse: timing.responseEnd - timing.requestStart,
domProcessing: timing.domLoading - timing.responseEnd
};
}
""")
# DNS查询时间
assert timing.get("dnsLookup", 0) < 500, \
f"DNS查询时间 {timing.get('dnsLookup')}ms 超过500ms阈值"
# TCP连接时间
assert timing.get("tcpConnection", 0) < 500, \
f"TCP连接时间 {timing.get('tcpConnection')}ms 超过500ms阈值"
# 服务器响应时间
assert timing.get("serverResponse", 0) < 1000, \
f"服务器响应时间 {timing.get('serverResponse')}ms 超过1秒阈值"
@pytest.mark.performance
def test_resource_timing(self, home_page: HomePage):
"""测试资源加载时序"""
home_page.navigate()
resources = home_page.execute_js("""
() => {
const entries = performance.getEntriesByType('resource');
const scripts = entries.filter(e => e.initiatorType === 'script');
const styles = entries.filter(e => e.initiatorType === 'css');
return {
totalResources: entries.length,
scriptCount: scripts.length,
styleCount: styles.length,
totalDuration: entries.reduce((sum, e) => sum + e.duration, 0)
};
}
""")
assert resources.get("totalResources", 0) > 0, "未检测到资源加载"
home_page.logger.info(
f"资源统计: 共{resources.get('totalResources')}个资源,"
f"脚本{resources.get('scriptCount')}个,"
f"样式{resources.get('styleCount')}"
)
@pytest.mark.performance
def test_form_submission_time(self, contact_page: ContactPage, test_data_generator):
"""测试表单提交时间"""
contact_page.navigate()
data = test_data_generator.generate_contact_form_data(use_valid=True)
start_time = time.time()
contact_page.fill_contact_form(data)
contact_page.submit_form()
try:
contact_page.verify_form_submission_success()
except Exception:
pass
end_time = time.time()
duration = (end_time - start_time) * 1000
# 阈值:30秒(开发环境可能较慢)
assert duration < 30000, f"表单提交耗时 {duration:.2f}ms 超过30秒阈值"
@pytest.mark.performance
def test_scroll_performance(self, home_page: HomePage):
"""测试滚动性能"""
home_page.navigate()
# 执行多次滚动
scroll_times = []
for i in range(5):
start_time = time.time()
home_page.scroll_to_bottom()
home_page.scroll_to_top()
end_time = time.time()
scroll_times.append((end_time - start_time) * 1000)
avg_scroll_time = sum(scroll_times) / len(scroll_times)
# 平均滚动时间应该在1秒内
assert avg_scroll_time < 1000, f"平均滚动时间 {avg_scroll_time:.2f}ms 超过1秒阈值"
@pytest.mark.performance
def test_element_visibility_performance(self, home_page: HomePage):
"""测试元素可见性检查性能"""
home_page.navigate()
elements = [
"header",
"main",
"footer",
"h1",
"nav",
"section:first-of-type",
"form",
"button"
]
check_times = []
for element in elements:
start_time = time.time()
try:
home_page._is_visible(element)
except Exception:
pass
end_time = time.time()
check_times.append((end_time - start_time) * 1000)
avg_check_time = sum(check_times) / len(check_times)
# 单个元素检查时间应该在3000ms内(开发环境)
assert avg_check_time < 3000, f"平均元素检查时间 {avg_check_time:.2f}ms 超过3000ms阈值"
@pytest.mark.performance
def test_navigation_performance(self, home_page: HomePage, contact_page: ContactPage):
"""测试导航性能"""
# 测量导航到联系页面的时间
start_time = time.time()
contact_page.navigate()
contact_page.wait_for_load()
end_time = time.time()
nav_time = (end_time - start_time) * 1000
# 阈值:3秒
assert nav_time < 3000, f"导航时间 {nav_time:.2f}ms 超过3秒阈值"
@pytest.mark.performance
@pytest.mark.responsive
def test_performance_across_viewports(self, home_page: HomePage):
"""测试不同视口下的性能"""
viewports = [
(375, 667, "移动端"),
(768, 1024, "平板端"),
(1920, 1080, "桌面端")
]
results = []
for width, height, name in viewports:
home_page.page.set_viewport_size({"width": width, "height": height})
start_time = time.time()
home_page.navigate()
home_page.wait_for_load()
end_time = time.time()
load_time = (end_time - start_time) * 1000
results.append((name, load_time))
home_page.logger.info(f"{name} ({width}x{height}): {load_time:.2f}ms")
# 验证所有视口加载时间在阈值内
for name, load_time in results:
assert load_time < 5000, f"{name}加载时间 {load_time:.2f}ms 超过5秒阈值"