当前位置: 首页 > news >正文

ccf接口测试实战

一、比赛题目

练习赛-测试人网站搜索接口自动化测试 - 霍格沃兹测试开发学社 / 霍格沃兹测试学院教务处 - 爱测-测试人社区

二、分析题目

一、题目解析

✅ 核心任务:

使用 Python 编写接口自动化测试脚本,对 https://ceshiren.com/search 接口进行全面测试,并打包成 .zip 文件提交。


🔍 题目关键信息提取:

项目内容
被测接口GET https://ceshiren.com/search
请求方式GET
必须 header"Accept": "application/json"
关键参数q(搜索关键词)、limit(返回条数)、term(用户搜索字段)
测试重点搜索功能的 正确性 和 稳定性
筛选条件支持分类、标签、发帖人等(但在当前接口中未体现,暂不处理)
提交要求只接受 .zip 压缩包,语言限 Python 或 Java
评分标准用例覆盖、参数化、断言、封装、PO模式、token复用、代码规范

🧩 二、你应该怎么做?(分步指导)

✅ 第一步:理解接口行为

访问这个页面: 👉 https://ceshiren.com/search?expanded=true

这是「高级搜索」界面,你可以输入关键词、选择分类等。

但注意:接口层面目前只提供了 q 参数作为主要搜索字段

你可以手动测试几个请求:

# 示例请求
GET https://ceshiren.com/search?q=pytest&limit=10

返回的是 JSON 格式数据,包含:

  • posts: 匹配的帖子列表(搜索结果应为空列表,表示没有找到相关内容,不传page,post不为空
  • topics: 匹配的话题
  • groups: 匹配的群组
  • users: 匹配的用户
  • grouped_search_result: 聚合结果(不传参数时,缺少q,搜索关键词为空,响应不应包含 grouped_search_resul

✅ 第二步:设计测试用例(等价类 + 边界值)

📋 测试用例表
编号用例标题请求参数预期结果断言逻辑测试类型
TC01使用有效关键词能返回非空搜索结果q=pytestpage=1<br>(其他关键词类似)返回包含 posts 的 JSON 数据,且结果数量 > 0

1. 响应状态码为 200(由 r.raise_for_status() 保证)

2. 响应中包含 "posts" 字段3. len(posts) > 0

正向
TC02搜索关键词为空字符串时不应返回聚合结果q=""page=1响应中 grouped_search_result 字段为 null 或不存在r.json().get("grouped_search_result") is None负向 / 边界
TC03搜索无匹配内容的关键词返回空结果列表q="oooooooooo"page=1posts 字段存在且为空数组 []r.json().get("posts") == []负向
TC04不传 q 参数时不应返回聚合搜索结果params={"page":1}(无 qgrouped_search_result 字段为 null 或不存在r.json().get("grouped_search_result") is None负向 / 缺省校验
TC05不传 page 参数时默认返回第一页结果q="测试用例"(无 page成功返回搜索结果,posts 非空len(r.json().get("posts")) != 0正向 / 默认值测试
TC06完全不传任何参数时不应触发有效搜索无任何参数grouped_search_result 字段为 null 或不存在r.json().get("grouped_search_result") is None负向 / 边界

✅ 第三步:满足评分标准(非常重要!)

这是你拿高分的关键,必须逐项满足:

评分项如何实现?✅
1. 业务流程完整实现“搜索 → 获取结果 → 断言”的完整流程
2. 用例参数化使用 @pytest.mark.parametrize
3. 场景覆盖全面包含正向、负向、边界、异常场景(见上表)
4. 代码规范(PEP8)使用驼峰/下划线命名、空行、注释、函数分离
5. 是否使用 PO 模式✅ 必须使用 Page Object 模式封装!
6. 是否有断言每个测试都有 assert 判断结果
7. token 是否复用当前接口无需登录 → 无 token → 可忽略
8. 代码是否封装抽取公共方法(如请求封装、base_url)

🏗️ 三、推荐架构:使用 PO(Page Object)设计模式

虽然这是接口测试,但也可以用 API 层的 PO 模式 来组织代码。

📁 项目结构建议:

三、代码

1.线性版本(简化版)

import pytest
import requestsclass TestCeshirenSearch:def setup_class(self):self.base_url = "https://ceshiren.com"# 搜索接口 urlself.search_url = f"{self.base_url}/search"# 正向用例@pytest.mark.parametrize("search_key",["pytest","面试题","a","appium desktop连接真机,start session,出现报错,手机上appium setting打开闪退,但是进程显示是进行中。报错内容:An unknown server-side error occurred while processing the command. Original error: Could not find a connected Android device in 20364ms.",])def test_search(self, search_key):params = {"q": search_key,"page": 1}headers = {"Accept": "application/json"}r = requests.request(method="GET", url=self.search_url, params=params, headers=headers)print(r.text)results = len(r.json().get("posts"))print(f"响应结果中 posts 结果数量为 {results}")assert results != 0# 搜索关键词为空def test_search_none(self):params = {"q": "","page": 1}headers = {"Accept": "application/json"}r = requests.request(method="GET", url=self.search_url, params=params, headers=headers)print(r.text)assert r.json().get("grouped_search_result") == None# 搜索结果为空def test_search_no_result(self):params = {"q": "ooooooooooo","page": 1}headers = {"Accept": "application/json"}r = requests.request(method="GET", url=self.search_url, params=params, headers=headers)print(r.text)assert r.json().get("posts") == []# 缺少请求参数 qdef test_search_noq(self):params = {"page": 1}headers = {"Accept": "application/json"}r = requests.request(method="GET", url=self.search_url, params=params, headers=headers)print(r.text)assert r.json().get("grouped_search_result") == None# 缺少请求参数 pagedef test_search_nopage(self):params = {"q": "测试用例"}headers = {"Accept": "application/json"}r = requests.request(method="GET", url=self.search_url, params=params, headers=headers)print(r.text)results = len(r.json().get("posts"))print(f"响应结果中 posts 结果数量为 {results}")assert results != 0# 不传请求参数def test_search_noparams(self):headers = {"Accept": "application/json"}r = requests.request(method="GET", url=self.search_url, headers=headers)print(r.text)assert r.json().get("grouped_search_result") == None

✅ 整体优点总结

优点说明
🟢 结构清晰类封装 + setup_class 初始化
🟢 数据驱动@pytest.mark.parametrize 提高测试覆盖率
🟢 覆盖全面正向、边界、异常、缺失参数等场景
🟢 断言合理根据不同场景设置不同预期结果
🟢 日志输出print(r.text) 便于调试(生产环境建议改为 logging)

⚠️ 潜在问题与改进建议

1. ❌ 缺少异常处理(健壮性不足)

当前代码未捕获网络异常或 JSON 解析错误,可能导致测试崩溃。

✅ 改进建议:
import requests
from requests.exceptions import RequestException
import jsontry:r = requests.get(...)r.raise_for_status()  # 检查 HTTP 状态码json_data = r.json()
except RequestException as e:pytest.fail(f"请求失败: {e}")
except json.JSONDecodeError:pytest.fail("响应不是合法的 JSON")

2. ❌ 断言不够精确(部分场景)
assert r.json().get("grouped_search_result") == None

更推荐使用 is None 判断:

assert r.json().get("grouped_search_result") is None

.get() 返回 None 是明确的空值,应使用 is 而非 ==


3. ❌ 缺少对 HTTP 状态码的校验

接口可能返回 404500,但代码只解析 JSON,容易误判。

assert r.status_code == 200

4. ❌ print(r.text) 不适合 CI/CD 环境

在自动化流水线中,print 输出不易管理。

import logging
logging.basicConfig(level=logging.INFO)
logging.info(r.text)

或者使用 pytest 的 -s 结合 logging


5. ✅ 可增加更多边界测试(建议扩展)

目前覆盖不错,但仍可补充:

场景建议
特殊字符搜索q="!@#$%^&*"
空格开头/结尾q=" pytest "
超长关键词>1000 字符
SQL注入/XSS尝试如 q="<script>alert(1)</script>"(验证前端过滤)
大小写敏感性"Pytest" vs "pytest"

6. ✅ 可引入 fixture 优化重复代码

当前每个方法都重复定义 headersrequests.get

@pytest.fixture
def client(self):session = requests.Session()session.headers.update({"Accept": "application/json"})return sessiondef test_search(self, client, search_key):params = {"q": search_key, "page": 1}r = client.get(self.search_url, params=params)...

7. ✅ 建议添加接口响应时间监控(性能维度)
assert r.elapsed.total_seconds() < 2  # 响应时间小于 2 秒

优化版本

import pytest
import requests
import timeclass TestCeshirenSearch:def setup_class(self):self.base_url = "https://ceshiren.com"self.search_url = self.base_url + "/search"@pytest.fixturedef client(self):"""封装请求客户端,复用 headers 和 session"""session = requests.Session()session.headers.update({"Accept": "application/json"})return session@pytest.mark.parametrize("search_key",["pytest","面试题","a","appium desktop连接真机,start session,出现报错,手机上appium setting打开闪退",])def test_search(self, search_key, client):params = {"q": search_key, "page": 1}# 添加延迟,避免触发限流time.sleep(1)# 发送请求r = client.get(self.search_url, params=params, timeout=10)# 新增:显式断言状态码为 200assert r.status_code == 200, f"HTTP 状态码错误: {r.status_code}"# 或者也可以保留 r.raise_for_status(),但你明确要求用 assert,所以用 assertjson_data = r.json()assert "posts" in json_data, "响应缺少 posts 字段"results = len(json_data["posts"])print(f"关键词 '{search_key}' 的搜索结果数: {results}")assert results > 0, f"搜索 '{search_key}' 未返回结果"def test_search_empty_keyword(self, client):"""测试空关键词搜索"""params = {"q": "", "page": 1}r = client.get(self.search_url, params=params, timeout=10)assert r.status_code == 200json_data = r.json()# Discourse 在 q="" 时不会返回 grouped_search_resultassert json_data.get("grouped_search_result") is Nonedef test_search_no_results(self, client):"""测试无结果的关键词"""params = {"q": "nonexistentkeyword12345", "page": 1}r = client.get(self.search_url, params=params, timeout=10)assert r.status_code == 200json_data = r.json()assert len(json_data["posts"]) == 0

2.po设计模式 

预告

http://www.dtcms.com/a/309196.html

相关文章:

  • 机器学习sklearn:编码、哑变量、二值化和分段
  • Implement recovery based on PITR using dump file and binlog
  • 用离子交换树脂做镍钴分离的工艺优势
  • Solana:解决Anchor Build编译程序报错 no method named `source_file` found for struct
  • 暑期算法训练.12
  • 练习javaweb+mysql+jsp
  • 渗透测试常用指令
  • [vue3 echarts] echarts 动态数据更新 setInterval
  • winform,DataGridView单元格点击选择日期,日期控件
  • 使用 whisper, 音频分割, 整理需求 2
  • 高防服务器租用:保障数据安全
  • 【智能Agent场景实战指南 Day 29】Agent市场趋势与前沿技术
  • 法国彩虹重磅发布EmVue:解锁能源监控新方式
  • TGD第十篇:当神经网络遇到TGD特征
  • 相亲小程序个人资料管理系统模块搭建
  • 数据结构(10)栈和队列算法题
  • 25电赛e题杂乱环境稳定识别矩形框(附源码)
  • 浏览器环境segmentit实现中文分词
  • 精通分类:解析Scikit-learn中的KNN、朴素贝叶斯与决策树(含随机森林)
  • LLM Prompt与开源模型资源(2)提示工程关键技术
  • 工程化(二):为什么你的下一个项目应该使用Monorepo?(pnpm / Lerna实战)
  • 位运算-面试题01.01.判定字符是否唯一-力扣(LeetCode)
  • 【unity小技巧】封装unity适合2D3D进行鼠标射线检测,获取鼠标位置信息检测工具类
  • 8.1每日一题
  • (线段树)SP2916 GSS5 / nfls #2899 查询最大子段和 题解
  • STL进阶典题整理 2025.7.30-2025.8.1
  • 关于继承的一些知识(C++)
  • react-native在mac的m2芯片下,pod install安装glog的时候报错
  • bmcweb工作流程
  • 【科研绘图系列】R语言绘制环状分组显著性柱状堆积图