1. 个人自动化测试问卷调查表的设计初衷
上周帮朋友公司排查一个线上问卷系统崩溃的问题时,发现他们还在用人工逐条核对测试数据。看着测试同事对着200多份问卷结果人工比对的场景,我突然意识到——这种重复劳动完全可以用自动化测试来解决。于是花了三天时间,用Python+Requests+Pytest搭建了一套轻量级的问卷调查自动化测试框架,测试效率提升了20倍不止。
这套方案特别适合中小型问卷系统、教育培训机构的随堂测试、企业内部调研等场景。相比商业化的测试平台,它的优势在于:
- 零成本部署(只需要Python环境)
- 完全可定制化(能灵活适配各种问卷格式)
- 测试数据可视化(自动生成带截图的测试报告)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心架构设计
2.1 技术选型对比
我对比了当前主流的几种自动化测试方案:
| 方案 | 适用场景 | 学习成本 | 执行效率 | 报告展示 |
|---|---|---|---|---|
| Selenium WebDriver | Web UI测试 | 中 | 低 | 一般 |
| Appium | 移动端测试 | 高 | 中 | 需定制 |
| Playwright | 跨浏览器测试 | 低 | 高 | 优秀 |
| Requests+Pytest | API/接口测试 | 低 | 极高 | 可定制 |
最终选择Requests+Pytest组合是因为:
- 问卷调查系统本质是表单提交和结果校验,属于API测试范畴
- Pytest的fixture机制非常适合处理测试数据驱动
- 结合Allure可以生成带请求/响应详情的可视化报告
2.2 框架目录结构
code复制survey_auto_test/
├── config/ # 配置文件
│ ├── survey_urls.yaml # 问卷链接配置
│ └── test_users.yaml # 测试账号数据
├── test_cases/ # 测试用例集
│ ├── basic_flow/ # 基础流程测试
│ └── edge_cases/ # 边界条件测试
├── utils/ # 工具类
│ ├── request_client.py # 封装请求方法
│ └── report_utils.py # 报告生成工具
└── conftest.py # Pytest全局fixture
关键技巧:用yaml而不是json管理测试数据,可以添加注释说明每个字段用途,维护更友好
3. 核心实现细节
3.1 问卷提交自动化
通过Chrome开发者工具抓包分析,发现问卷提交的核心请求:
python复制# utils/request_client.py
def submit_survey(survey_id, answers):
url = f"https://api.survey.com/v1/{survey_id}/responses"
headers = {
"Content-Type": "application/json",
"X-Auth-Token": os.getenv("API_TOKEN")
}
payload = {
"respondent_id": generate_uuid(),
"answers": answers,
"metadata": {
"ip_address": fake.ipv4(),
"user_agent": fake.user_agent()
}
}
return requests.post(url, json=payload, headers=headers)
这里有几个关键点:
- 使用环境变量存储API_TOKEN避免硬编码
- 通过faker库生成仿真测试数据
- respondent_id需要符合问卷系统的UUID格式要求
3.2 测试数据驱动
在conftest.py中定义数据驱动fixture:
python复制# conftest.py
import pytest
import yaml
@pytest.fixture(params=yaml.safe_load(open("config/test_users.yaml")))
def test_user(request):
user = request.param
user["answers"] = generate_answers(user["user_type"])
return user
def generate_answers(user_type):
"""根据用户类型生成不同的答案组合"""
if user_type == "normal":
return {qid: random.choice(options) for qid, options in QUESTIONS.items()}
elif user_type == "malicious":
return {qid: "XSS<script>alert(1)</script>" for qid in QUESTIONS}
这样在测试用例中只需声明使用test_user fixture,就能自动运行所有测试用户组合。
4. 高级测试场景实现
4.1 验证问卷逻辑跳转
很多问卷会根据前一题的答案跳转到不同问题,测试时需要验证跳转逻辑是否正确:
python复制# test_cases/basic_flow/test_question_routing.py
def test_question_routing(test_user):
# 提交特定答案触发跳转
answers = {"Q1": "OptionA"}
response = submit_survey(TEST_SURVEY_ID, answers)
# 验证返回的下一个问题符合预期
next_questions = response.json()["next_questions"]
assert "Q3" in next_questions # 应该跳过Q2
assert "Q2" not in next_questions
4.2 性能测试集成
用pytest-benchmark插件添加性能测试:
python复制# test_cases/edge_cases/test_performance.py
def test_submit_performance(benchmark):
@benchmark
def submit_100_requests():
for _ in range(100):
submit_survey(TEST_SURVEY_ID, generate_answers())
assert submit_100_requests.stats["max"] < 1.0 # 100次请求最大耗时<1秒
5. 测试报告优化
5.1 Allure报告增强
在conftest.py中添加钩子函数增强Allure报告:
python复制# conftest.py
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if report.when == 'call':
# 自动附加请求/响应到报告
if "request_client" in item.fixturenames:
request = item.funcargs["request"]
allure.attach(
str(request.response.text),
name="API Response",
attachment_type=allure.attachment_type.TEXT
)
5.2 自动生成测试摘要
python复制# utils/report_utils.py
def generate_executive_summary():
stats = {
"pass_rate": (passed / total) * 100,
"avg_response_time": sum(times) / len(times),
"critical_issues": len([i for i in issues if i["level"] == "critical"])
}
with open("summary.md", "w") as f:
f.write(f"""## 测试执行摘要
- 通过率: {stats['pass_rate']:.2f}%
- 平均响应时间: {stats['avg_response_time']:.2f}ms
- 严重问题: {stats['critical_issues']}个
""")
6. 常见问题排查
6.1 验证码处理方案
遇到验证码时有几种解决方案:
- 测试环境关闭验证码(推荐)
- 使用第三方打码平台(成本高)
- 设置白名单IP跳过验证(需要运维配合)
python复制# utils/request_client.py
def bypass_captcha():
if os.getenv("ENV") == "test":
return {"captcha": "TEST_MODE"}
else:
# 调用打码平台API
return {"captcha": decode_captcha()}
6.2 测试数据清理
自动化测试会产生大量测试数据,需要在teardown阶段清理:
python复制# conftest.py
@pytest.fixture(scope="session", autouse=True)
def cleanup_test_data():
yield
if os.getenv("CLEANUP") == "true":
delete_test_responses(TEST_SURVEY_ID)
7. 持续集成部署
7.1 GitHub Actions配置
yaml复制# .github/workflows/test.yml
name: Survey Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- run: pip install -r requirements.txt
- run: pytest --alluredir=./allure-results
- uses: actions/upload-artifact@v2
if: always()
with:
name: allure-results
path: ./allure-results
7.2 钉钉通知集成
python复制# utils/notify.py
def send_dingtalk_message(report_url):
headers = {"Content-Type": "application/json"}
payload = {
"msgtype": "markdown",
"markdown": {
"title": "测试结果通知",
"text": f"### 问卷调查自动化测试完成\n"
f"- 通过率: {pass_rate}%\n"
f"- 查看详情: [报告链接]({report_url})"
}
}
requests.post(DINGTALK_WEBHOOK, json=payload, headers=headers)
这套框架在我最近参与的三个问卷项目中,平均减少了80%的测试时间。最惊喜的是发现了人工测试很难触发的边界条件问题——比如当用户同时提交两份完全相反的答案时,后台统计结果会出现负数。建议在fixture中多设计一些异常数据组合,往往能发现隐藏很深的逻辑漏洞。
