【Django】-10- 单元测试和集成测试(下)

五、匿名和登录测试

🌟 第一部分:自动登录测试(user_client fixture + 登录态测试)

这部分是测试 **"登录后才能访问的页面"**,用 force_login 模拟用户登录,验证 "未登录 vs 已登录" 的不同响应~

1. user_client fixture ------ 自动登录的 "魔法工具"
python 复制代码
@pytest.fixture()  
def user_client(client: Client, user):  
    client.force_login(user)  # 模拟用户登录  
    return client  

这个**user** 参数会自动 "召唤" 咱们之前定义的 user fixture------ 也就是那个提前在数据库里创建好的 test_user 测试用户~

它的作用就像 "递道具":
user_client 需要一个 "真实存在的用户" 来模拟登录,而 user fixture 正好提前准备好了这个用户,直接拿来就能用~ 不用再手动创建啦!

  • @pytest.fixture(): pytest 的 "魔法标记"🧙,把 user_client 变成可复用的工具。
  • def user_client(client: Client, user):
    • client 是 Django 测试客户端(虚拟小浏览器),user 是之前 user fixture 创建的测试用户。
    • 函数名 user_client 是这个工具的名字,其他测试用例可以直接用它!
  • client.force_login(user)
    • 核心逻辑:模拟用户登录,相当于 "用测试用户自动登录,不用输密码"~
    • 效果:之后用 user_client 发请求时,会带着 "已登录" 的身份!
  • return client:把 "已登录的虚拟浏览器" 返回,其他测试用例可以直接用~
2. test_submit_anonymous ------ 测试 "未登录用户访问"
python 复制代码
def test_submit_anonymous(client):  # 未登录的客户端  
    resp: HttpResponse = client.get("/beifan/submit")  
    assert resp.status_code == 302  
  • def test_submit_anonymous(client):
    • 参数 client 是 "未登录的虚拟浏览器"(因为没用 user_client)。
    • 测试目标:未登录用户访问 /beifan/submit 会怎样?
  • resp: HttpResponse = client.get("/beifan/submit")
    • 用 "未登录浏览器" 发 GET 请求,访问提交反馈的页面。
  • assert resp.status_code == 302
    • 302 是 "重定向" 状态码~ 说明未登录用户访问需要登录的页面时,会被重定向到登录页(符合预期!)
3. test_submit_user ------ 测试 "已登录用户访问"
python 复制代码
def test_submit_user(user_client):  # 已登录的客户端  
    resp: HttpResponse = user_client.get("/beifan/submit")  
    assert resp.status_code == 200  

    html = resp.content.decode()  
    assert "速度" in html  
  • def test_submit_user(user_client):
    • 参数 user_client 是 "已登录的虚拟浏览器"(因为用了 user_client fixture)。
    • 测试目标:已登录用户访问 /beifan/submit 会怎样?
  • resp: HttpResponse = user_client.get("/beifan/submit")
    • 用 "已登录浏览器" 发 GET 请求,访问提交反馈的页面。
  • assert resp.status_code == 200
    • 200 是 "成功" 状态码~ 说明已登录用户可以正常访问!
  • html = resp.content.decode()
    • 把响应内容(二进制)转成字符串,方便检查页面内容。
  • assert "速度" in html
    • 检查页面里是否有 "速度" 这个词,确保页面内容符合预期~

🌟 第二部分:参数化测试(test_result_all)------ 批量测试 "不同请求方法"

这部分用 @pytest.mark.parametrize 批量测试不同 HTTP 方法(GET/POST/DELETE 等)访问页面的情况~

python 复制代码
@pytest.mark.parametrize(  
    "method",  
    [  
        "get",  
        "post",  
        "delete",  
        "put",  
        "beifan"  # 注意:不是合法 HTTP 方法 但就是能成功请求  
    ]  
)  
def test_result_all(client, method):  
    resp: HttpResponse = client.generic(method.upper(), "/beifan/result")  
    assert resp.status_code == 200  
  • @pytest.mark.parametrize("method", [...])
    • pytest 的 "参数化魔法"🪄!批量生成测试用例,method 会依次取列表里的值(get、post、delete、put、beifan)。
    • 作用:用不同的 HTTP 方法访问 /beifan/result,看是否都返回 200。
  • def test_result_all(client, method):
    • client 是虚拟浏览器,method 是当前要测试的 HTTP 方法。
  • resp: HttpResponse = client.generic(method.upper(), "/beifan/result")
    • client.generic(...) 是 Django 测试客户端的 "万能方法",可以发任意 HTTP 方法的请求。
    • method.upper():把方法名转成大写(因为 HTTP 方法规范是大写,比如 GETPOST)。
    • 效果:依次用 GET、POST、DELETE、PUT、BEIFAN(注意:beifan 不是合法 HTTP 方法但能行)访问 /beifan/result
  • assert resp.status_code == 200
    • 断言不管用哪种方法访问,返回状态码都是 200(假设页面允许这些方法)。

🌈 整体流程总结

  1. 自动登录工具user_client fixture 帮你模拟用户登录,返回 "已登录的虚拟浏览器"。
  2. 测试登录态
    • 未登录用户访问需要登录的页面 → 重定向(302)。
    • 已登录用户访问 → 成功(200),还能检查页面内容。
  3. 批量测试请求方法 :用 parametrize 批量测试不同 HTTP 方法访问页面,确保都能正常响应~

为什么要用client.generic(...)而不能用反射直接写method占位

为啥用 client.generic(...) 而不是 "反射占位"~ 就像在问:"为啥点外卖要用平台,不能自己挨个打电话呀?"

🍔 核心原因 1:client.generic 是 "官方外卖平台",稳!

Django 测试客户端的 client.generic(method, url)官方专门设计的 "万能请求方法",它帮你做好了所有 "请求规范" 的事儿:

  • 自动处理 HTTP 方法的大小写(比如你传 'get',它会转成 'GET'
  • 自动构造符合 HTTP 协议的请求(请求头、请求体格式等)
  • 兼容所有合法 HTTP 方法(GET/POST/PUT/DELETE 等)

而 "反射 + 手动拼接" 就像 "自己挨个给餐厅打电话",容易出错(比如方法名没大写、请求头忘加、协议格式不对)~

🛠️ 对比:用 client.generic vs 手动写 "反射"

假设你想动态发请求,不用 client.generic,尝试 "反射" 写:

❌ 手动写法(容易翻车):
python 复制代码
def test_result_all(client, method):  
    # 假设用 getattr 反射  
    method_func = getattr(client, method.lower())  # 比如 client.get 或 client.post  
    resp = method_func("/beifan/result")  # 调用方法  
    assert resp.status_code == 200  

问题 1:不是所有方法都能直接调用

Django 测试客户端的方法(client.get/client.post 等)有严格的参数要求:

  • 比如 client.get 主要传 urlparams(查询参数)
  • client.post 还要传 data(请求体)、content_type

如果用反射调用,遇到 PUT/DELETE 这类方法,参数传不对就会报错~

问题 2:不支持自定义 / 非标准方法

比如你参数里写了 'beifan'(虽然它不是合法 HTTP 方法),client.beifan 根本不存在,反射会直接报错:
AttributeError: 'Client' object has no attribute 'beifan'

client.generic 不管方法是否合法,都会尝试发请求(虽然服务器可能返回 405 方法不允许,但测试能覆盖这种情况)~

✅ 用 client.generic(稳如外卖平台):
python 复制代码
resp = client.generic(method.upper(), "/beifan/result")  
  • 不管 method'get'/'post'/'delete' 还是乱写的 'beifan',它都会:
    1. 转成大写(method.upper()
    2. 构造一个 HTTP 请求,带着这个方法名发出去
    3. 服务器收到后,按实际支持的方法处理(支持就返回 200,不支持返回 405 等)

这就像外卖平台帮你处理 "商家是否接单""配送是否合规",你只需要填方法和地址就行~

🌟 总结:client.generic 是 "万能请求通道"

它的存在就是为了简化 "任意 HTTP 方法请求" 的测试,帮你避免手动调用方法时的参数坑、方法不存在的坑、协议规范的坑~

而 "反射 + 手动调用" 就像 "自己跑腿送外卖",容易漏步骤、出问题~

所以 Django 官方贴心地给了 client.generic 这个 "万能工具",咱当然优先用它啦! 🛠️

六、🎯 统计测试覆盖率:给代码做 "体检" 啦~

这行命令 --cov=beifan --cov-report html --cov-fail-under=95 就像给你的代码请了个 "体检医生" ,每个参数都是体检步骤:

1. --cov=beifan

👉 告诉 pytest:"我要体检的项目是 beifan 这个 app 里的代码!"

beifan 想象成 "体检科室" ,只检查这个科室里的代码哪些被测试覆盖了~

2. --cov-report html

👉 生成超可爱的 HTML 报告 !测试跑完后,会自动生成一个网页,点进去能看到:

  • 哪些代码行被测试覆盖了(绿色✔️)
  • 哪些代码行 "躺平" 没被测试(红色❌)
    像玩 "找不同" 游戏,一眼发现代码里的 "漏网之鱼"~

3. --cov-fail-under=95

👉 给体检定个 "及格线":测试覆盖率必须≥95%

如果覆盖率不够(比如只有 90%),测试直接 "不及格" ❌ ,逼着你补全测试用例~

🌟 总结:

这就像给代码开 "健康训练营" :

  • 先用 --cov 做体检,生成报告看哪里 "不健康"
  • 补全测试用例,让代码覆盖率达标
  • 最后所有测试通过,代码才能 "毕业" !

快动手补测试,让你的 Django 项目健健康康~ 🚀

七、🎮 Django 单元测试闯关思路

1. 🏗️ 搭建测试环境:准备 "测试小道具"

  • 依赖安装 :用 pdm 装测试工具(pytest、pytest-django 等),像给游戏买 "辅助道具"
  • 配置文件 :写 .flake8(代码风格检查)、pytest.ini(pytest 配置),给测试定 "规则"

2. 📝 设计测试用例:规划 "闯关路线"

  • 拆分测试点:把功能拆成小关卡(注册、登录、提交反馈等)
  • 覆盖场景:每个关卡测全场景(成功、失败、边界情况),比如注册要测 "用户名空""密码不一致""注册成功"

3. 🛠️ 写测试代码:用工具 "闯关"

  • fixture 复用 :用 @pytest.fixture 做 "通用道具"(比如 user 造测试用户、user_client 模拟登录),避免重复劳动
  • 发请求测试 :用 Django 测试客户端 client 发 HTTP 请求(get/post 等),模拟用户操作
  • 断言验证:检查响应状态码、内容、数据库变化,确保功能符合预期

4. 📊 统计覆盖率:验收 "闯关成果"

  • --cov 系列参数生成覆盖率报告,要求 ≥95%
  • 看 HTML 报告找 "漏测代码",补全测试用例,让代码 100% 被 "宠幸"

5. 🔄 迭代优化:反复 "刷关卡"

  • 测试不通过 → 修 bug、补用例
  • 覆盖率不够 → 补测试,直到每个文件达标
  • 最终所有测试通过、覆盖率达标,代码 "毕业" !

🌈 核心思想:

把 Django 功能拆成 "小关卡",用测试用例逐个攻破,用覆盖率验收成果~

就像玩闯关游戏,每通过一关、补全一个漏洞,代码质量就升级一级! 🚀

(简单说:拆分功能→写用例→跑测试→补漏洞→达标 ,循环往复直到代码 "无懈可击"~)

八、🎭 Django 集成测试:组件联演大派对!

把 Django 应用想象成一场 "舞台剧",集成测试就是让"演员(视图 / 模型 / 数据库 / 模板)" 集体彩排 **,验证他们配合是否默契~

1. 🎪 测试范围:"多角色联演",不单打独斗!

  • 单元测试 :像 "单人脱口秀",只测 1 个函数 / 模型(比如测 TodoItem.save() 方法)。
  • 集成测试 :像 "群演大合唱",把视图、模型、数据库、模板全拉到舞台上,一起测流程!

👉 比如测 "待办事项提交":

从用户点 "提交按钮"(视图接收 POST 请求)→ 数据存数据库(模型 + 数据库)→ 页面跳转并显示新事项(模板渲染),全流程联测

2. 🧑💻 测试方式:模拟真实 "用户操作"!

Django 测试客户端(Client)是你的 **"虚拟观众"**,帮你模拟用户行为:

① 发请求:client.get()/client.post()

👉 模拟用户 "访问页面""提交表单",像观众喊:"我要看待办列表!""我要提交新事项!"

② 数据库联动:自动读写测试库

👉 提交数据时,集成测试会真的往测试数据库里写数据(但会自动清理,不影响真实库),像舞台上的 "道具组" 配合换道具~

③ 模板渲染检查:看页面内容对不对

👉 用 response.content 检查模板渲染结果,像观众喊:"新事项有没有显示在页面上呀?"

3. 🛠️ 测试设置:搭好 "舞台环境"!

  • 测试数据库 :Django 会自动建一个临时数据库(用完就删),结构和真实库一样,像给彩排单独搭舞台~
  • 加载配置:中间件、URL 路由、设置全启用,像舞台灯光 / 音效全打开,模拟真实环境!

4. 📝 测试示例:待办事项 App 联演!

假设你有个待办 App,功能是 "提交事项 → 存数据库 → 页面显示",集成测试会这么写:

python 复制代码
import pytest  
from django.test import Client  
from todo.models import TodoItem  # 你的待办模型  

# 虚拟观众(测试客户端)  
@pytest.fixture  
def client():  
    return Client()  

# 测试:提交待办事项全流程  
def test_todo_submit_flow(client):  
    # 1. 模拟用户提交表单(发 POST 请求)  
    data = {"title": "给代码写测试!"}  
    response = client.post("/todo/submit/", data)  

    # 2. 检查是否跳转(状态码 302 代表重定向)  
    assert response.status_code == 302  

    # 3. 检查数据库是否真的存了数据  
    todo = TodoItem.objects.first()  
    assert todo.title == "给代码写测试!"  

    # 4. 检查页面是否显示新事项(访问列表页)  
    list_response = client.get("/todo/list/")  
    assert "给代码写测试!" in list_response.content.decode()  

👉 这个测试里,视图、模型、数据库、模板全联动了!像验证 "用户提交 → 数据存库 → 页面显示" 的完整流程~

5. 🎯 测试目的:确保 "演员配合默契"!

  • 单元测试保证 "单个演员演技好",集成测试保证 "所有演员配合好"~
  • 如果集成测试失败,说明组件间有 "配合漏洞"(比如视图存数据的逻辑和模型不一致,模板没正确渲染数据)。

🌈 总结:

Django 集成测试是 **"多组件联演测试"**,用虚拟客户端模拟用户操作,联动视图 / 模型 / 数据库 / 模板,验证全流程是否通顺~

就像舞台剧彩排:单个演员(单元测试)没问题 → 全体演员联演(集成测试)没问题 → 正式演出(上线)才稳! 🚀

相关推荐
茉莉玫瑰花茶8 分钟前
Redis 常用数据结构以及单线程模型
数据库·redis·缓存·bootstrap
RainbowSea9 分钟前
Windows 安装 RabbitMQ 消息队列超详细步骤(附加详细操作截屏)
后端
安冬的码畜日常9 分钟前
【AI 加持下的 Python 编程实战 2_13】第九章:繁琐任务的自动化(中)——自动批量合并 PDF 文档
人工智能·python·自动化·ai编程·ai辅助编程
RainbowSea10 分钟前
Windows 11家庭版安装 Docker
后端
_码农1213811 分钟前
Spring IoC容器与Bean管理
java·后端·spring
废喵喵呜15 分钟前
达梦数据库权限体系详解:系统权限与对象权限
数据库·sql·oracle
哈基咩22 分钟前
Go 语言模糊测试 (Fuzz Testing) 深度解析与实践
开发语言·后端·golang
mCell22 分钟前
告别轮询!深度剖析 WebSocket:全双工实时通信原理与实战
后端·websocket·http
@十八子德月生32 分钟前
第三阶段—8天Python从入门到精通【itheima】-143节(pyspark实战——数据计算——flatmap方法)
大数据·开发语言·python·数据分析·pyspark·好好学习,天天向上·question answer
孫治AllenSun35 分钟前
【Java】使用模板方法模式设计EasyExcel批量导入导出
java·python·模板方法模式