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

【Django】-9- 单元测试和集成测试(上)

一、Django 项目单元 & 集成测试准备 👇

依赖安装(给项目装 “测试小帮手”🍼)

pdm add -d black isort flake8 pytest pytest-django pytest-coverage  

👉 这行命令像在给项目 “采购” 测试工具:

  • black ✨ 自动格式化代码,让代码整齐得像排好队的小士兵
  • isort 🧹 帮你把 import 语句整理得明明白白
  • flake8 🔍 像代码 “小侦探”,揪出语法和风格问题
  • pytest 🧪 单元测试 “大主角”,跑测试用例超好用
  • pytest-django 🐍+🧪 让 pytest 能和 Django 好好 “交朋友”,测试 Django 项目
  • pytest-coverage 📊 看看测试覆盖了多少代码,心里有底

 

配置文件(给工具们定 “小规矩”📜)

1. .flake8(flake8 的 “小手册”)
[flake8]  
exclude = venv  # 告诉 flake8 别去碰 venv 文件夹~🚫  
extend-ignore = E501  # 放宽点啦,忽略行太长的报错(E501)🙈  
2. pytest.ini(pytest 的 “剧本”)
[pytest]  
DJANGO_SETTINGS_MODULE = Tesla.settings  # 告诉 pytest 用哪个 Django 配置⚙️  
python_files = tests.py test_*.py  # 哪些文件算测试文件呀?找 tests.py 和 test_开头的~🔍  

测试用例(给项目 “模拟闯关”🎮)

根据我们的业务逻辑代码进行分析~

 

1. 注册功能 📝
  • 允许匿名访问 👻(游客也能注册!)
  • URL:http://127.0.0.1:8000/accounts/register 🔗
    • GET:返回 HTML 页面~像打开注册 “小窗口”🖥️
    • POST:提交 JSON 数据,还要验证:
      • 用户名不能为空 ❌(空用户名像没名字的小幽灵,不行!)
      • 密码不能为空 ❌(没密码咋保护自己~)
      • 两次密码得一样 🔄(不然自己都记混啦)
      • 密码长度≥6 位 📏(太短不安全呀)
      • 用户名不能重复 👥(不能撞名呀)
      • 都对的话,返回 “注册成功”🎉

 

2. 登录功能 🔑
  • 允许匿名访问 👻(游客也能登录页逛逛)
  • URL:http://127.0.0.1:8000/accounts/login/ 🔗
    • GET:返回登录 HTML 页面 🖥️
    • POST:提交表单,还要验证:
      • 用户名不能为空 ❌(没用户名咋找账号~)
      • 密码不能为空 ❌(没密码进不去呀)
      • 用户名、密码得对 ✅(不然进错家门啦)

 

3. 提交反馈 📮
  • 不允许匿名访问 🔒(得登录才能反馈!)
  • URL:http://127.0.0.1:8000/beifan/submit 🔗
    • GET:返回 HTML 页面 🖥️
    • POST:提交 JSON 数据,还要验证:
      • 数据能存到数据库里 🗄️(像把信放进邮箱~)
      • 数据和用户关联上 👤(谁发的反馈要记好)
      • 同一用户不能重复发 🔄(别刷屏呀~)

4. 反馈结果 🔍
  • 允许匿名访问 👻(谁都能看看结果)
  • URL:http://127.0.0.1:8000/beifan/result 🔗
  • 不管 GET/POST,都返回 HTML 页面 🖥️(看看反馈结果啥样~)

二、HttpResponse

数据结构角度

HttpResponse定义了一系列属性和方法来管理响应相关的数据。

  • 属性方面
    • content:以字节串形式存储响应的主体内容,比如返回的 HTML 页面内容、JSON 数据经过编码后的字节串等。例如返回一个简单 HTML 页面,这个 HTML 文本内容最终会编码后存到content中。
    • status_code:记录 HTTP 响应状态码,像常见的200(请求成功)、404(页面未找到)、500(服务器内部错误)等,通过这个属性可以让客户端快速知晓请求处理的结果状态。
    • headers:是一个类似字典的数据结构,用来存放 HTTP 响应头信息,比如Content - Type(指定响应内容的类型,像text/html表示 HTML 页面,application/json表示 JSON 数据)、Content - Length(响应内容的长度)等。
  • 方法方面
    • 它提供了一些方法来操作响应数据,比如__setitem__方法(可以像操作字典一样response['key'] = 'value'),用于设置响应头信息。

 

面向对象角度

HttpResponse类遵循面向对象编程范式,通过封装、继承和多态等特性,来实现对 HTTP 响应的管理和扩展:

  • 封装:把与 HTTP 响应相关的各种信息(内容、状态码、响应头)和操作(设置响应头、获取内容等)封装在一个类中,提供了统一且便捷的接口来处理响应。比如在视图函数中,只需要创建HttpResponse实例并设置相关属性,就能轻松构建一个完整的 HTTP 响应。
  • 继承:Django 提供了一些HttpResponse的子类,如HttpResponseRedirect(用于重定向,默认状态码为302)、JsonResponse(专门用于返回 JSON 数据,自动设置Content - Typeapplication/json ) 。这些子类继承了HttpResponse的基本属性和方法,并根据自身功能需求进行了扩展和定制。
  • 多态:在 Django 的视图函数返回机制中,无论是返回HttpResponse对象还是它的子类对象,都遵循统一的规则(都被视为合法的响应返回值),这体现了多态性。视图函数根据业务逻辑的不同,灵活返回不同类型的响应对象,而 Django 的请求处理机制都能正确处理并发送给客户端。

 

Web 开发流程角度

在 Django 应用处理 HTTP 请求的流程中,HttpResponse是请求处理结果的最终承载者:

  • 当客户端发起一个 HTTP 请求到 Django 服务器,Django 会根据 URL 配置找到对应的视图函数进行处理。
  • 视图函数在处理完业务逻辑(如查询数据库、进行数据计算等)后,需要构建一个响应返回给客户端,此时就会创建HttpResponse对象(或者它的子类对象),将处理结果填充到响应对象的相关属性中(如设置响应内容、状态码、响应头)。
  • 最后,Django 的请求处理机制会将这个HttpResponse对象转换为符合 HTTP 协议规范的格式,通过网络发送给客户端,客户端再根据响应信息进行相应的展示或处理(如浏览器渲染 HTML 页面、解析 JSON 数据等) 。

总之,HttpResponse类是 Django 构建和管理 HTTP 响应的核心组件,通过数据结构、面向对象编程以及在 Web 开发流程中的关键作用,实现了从服务器端到客户端的响应信息传递。

三、测试HTTP请求 

先测试一个简单的登录视图的get请求(返回一个html页面)

from django.test.client import Clientimport pytest@pytest.fixture
def client() -> Client:return Client()def test_register_get(client: Client):resp: HttpResponse = client.get("/accounts/register")assert resp.status_code == 200html = resp.content.decode()assert "html" in htmlassert "用户名" in htmlassert "密码" in htmlassert "确认密码" in html

 

1. 引入工具:from django.test.client import Client

👉 作用:从 Django 测试工具里,把「发 HTTP 请求的小助手 Client」请进来~
👀 为啥?
Django 专门给咱准备了 Client 类,用来模拟浏览器发请求(比如 GET、POST),测试咱的视图函数 / 接口。就像给代码一个 “虚拟小浏览器”,不用真的开浏览器,也能测试网页 / 接口响不响应~

 

2. fixture 魔法:@pytest.fixture + def client() -> Client:

@pytest.fixture  
def client() -> Client:  return Client()  

👉 作用:用 pytest 的 fixture,创建一个可复用的 “发请求工具”,叫 client
👀 为啥这么写?

  • @pytest.fixture 是 pytest 的 “魔法标记”🧙,标记后,这个 client 函数就变成了一个 “工具工厂”,其他测试函数要用的时候,直接当参数传进去就行!
  • return Client():每次调用 client,都会新建一个 Client 实例(也就是新的 “虚拟小浏览器”),保证测试之间互不干扰~

 

3. 测试用例:def test_register_get(client: Client):

👉 作用:定义一个测试用例,名字叫 test_register_get,专门测试注册页面的 GET 请求
👀 为啥参数是 client: Client
因为上面用 @pytest.fixture 标记了 client,pytest 会自动把 Client 实例传进来,供这个测试用例使用!相当于 “自动给你递上小浏览器,不用自己手动创建啦”~

 

4. 发请求:resp: HttpResponse = client.get("/accounts/register")

👉 作用:用 client(虚拟小浏览器),发一个 GET 请求 到 /accounts/register(注册页面的 URL),然后把服务器返回的响应存到 resp 里~
👀 为啥这么写?
模拟用户在浏览器里输入 http://.../accounts/register 访问注册页的行为。client.get(...) 就是帮我们发 GET 请求的 “快捷方式”,不用真的启动浏览器~

 

5. 断言状态码:assert resp.status_code == 200

👉 作用:检查服务器返回的状态码是不是 200200 代表 “请求成功”,网页正常返回啦~)
👀 为啥要断言?
测试的核心!如果状态码不是 200(比如 404 找不到页面、500 服务器报错),说明注册页面可能有问题,测试就会 “失败”,提醒咱去修~

 

6. 解析响应内容:html = resp.content.decode()

👉 作用:把响应的二进制内容(resp.content)转换成字符串(decode() 解码),方便后面检查页面里有没有我们要的内容~
👀 为啥要解码?

网络编程(发送和接收网络数据包)的HttpResponse是字节流,二进制数据。
resp.content 存的是二进制数据(像 b'<html>...'),转成字符串(html)后,才能用字符串的方法(比如 in 关键字)检查内容~

 

7. 检查页面内容:一堆 assert

assert "html" in html  
assert "用户名" in html  
assert "密码" in html  
assert "确认密码" in html  

👉 作用:确认返回的 HTML 里,包含 “html”“用户名”“密码”“确认密码” 这些关键字~
👀 为啥要检查?
保证注册页面的 HTML 里,确实有这些表单字段(用户名、密码输入框)。如果哪天代码不小心把这些字段删了,测试就会失败,提醒咱 “注册页面不对啦!”

 

四、测试DB数据库 

 

🌟 user fixture —— 提前造个 “测试用户”

@pytest.fixture()  
def user(_django_db_helper):  new_user = User.objects.create_user(  username='test_user',  email='test_user@qq.com',  password='test_user_pass',  )  return new_user  
逐行拆解
  • @pytest.fixture()

    • pytest 的 “魔法标记”🧙,标记后,user 就变成一个可复用的 “工具函数”,其他测试用例要用时,直接传参即可!
    • 作用:提前帮你在数据库里造一个测试用户,不用每次测试都手动创建啦~
  • def user(_django_db_helper):

    • _django_db_helper 是 pytest-django 提供的 “数据库小助手”,会自动帮你初始化、清理数据库,保证测试间互不干扰~
    • 函数名 user 是你给这个 “造用户工具” 起的名字,方便其他测试用例调用~
  • new_user = User.objects.create_user(...)

    • 调用 Django 的 create_user 方法,在数据库里实际创建一个用户(用户名、邮箱、密码都是测试用的假数据~)
    • 相当于:“嘿,数据库~ 帮我塞一条用户数据,测试时要用!”
  • return new_user

    • 把刚创建的用户对象返回,其他测试用例如果用了这个 user fixture,就能直接拿到这个 “测试用户” 啦~

 

🌟 参数化测试 —— 批量测 “注册场景”

这部分是用 @pytest.mark.parametrize 批量测试不同注册情况(用户名空、密码不一致、注册成功等),超高效!

@pytest.mark.parametrize(  "data, code, msg",  [  ({"username": ""}, -1, "username 不能为空"),  # 用户名空  ({"password_confirm": "2"}, -2, "两次密码输入不一致"),  # 密码不一致  ({"username": "test_user_beifan"}, 0, "注册成功"),  # 注册成功  ]  
)  
def test_register_post(user, client, data, code, msg):  # 发 POST 请求测试注册  resp = client.post(  "/accounts/register",  data=data,  content_type="application/json"  )  # 解析响应  html = resp.content.decode()  resp_json = json.loads(html)  # 断言响应是否符合预期  assert resp_json["code"] == code  assert resp_json["msg"] == msg  

这里的data有简化省略了其他的键值对 

逐行拆解
  • @pytest.mark.parametrize("data, code, msg", [...])

    • pytest 的 “参数化魔法”🪄!括号里的 "data, code, msg" 是 “参数名”,后面的列表是 “参数值组合”。
    • 作用:批量生成测试用例,列表里每一个元组,都会对应一条测试用例~ 比如:
      • 第 1 组:data 是 {"username": ""}(用户名空),预期 code=-1msg="username 不能为空"
      • 第 2 组:data 是 {"password_confirm": "2"}(密码不一致),预期 code=-2msg="两次密码输入不一致"
      • 第 3 组:data 是 {"username": "test_user_beifan"}(合法数据),预期 code=0msg="注册成功"
  • def test_register_post(user, client, data, code, msg):

    • 测试用例函数,参数里:
      • user:就是图 1 里的 user fixture,会自动传入 “测试用户”(如果需要的话~)
      • client:Django 测试客户端(图 1 里讲过的 “虚拟小浏览器”)
      • data, code, msg:来自 @pytest.mark.parametrize 的参数,每组数据都会跑一次测试~
  • resp = client.post(...)

    • 用 client(虚拟小浏览器)发一个 POST 请求 到 /accounts/register(注册接口),还带了 data(请求体)和 content_type="application/json"(告诉服务器,我发的是 JSON 数据哟~)
  • html = resp.content.decode() → resp_json = json.loads(html)

    • 把响应的二进制内容(resp.content)解码成字符串(html),再转成 JSON(resp_json),方便断言~
  • assert resp_json["code"] == code → assert resp_json["msg"] == msg

    • 检查响应的 code 和 msg 是否符合预期~ 比如:
      • 用户名空时,code 应该是 -1msg 是 username 不能为空
      • 注册成功时,code 是 0msg 是 注册成功

 

🌟 数据库断言 —— 注册成功后,用户真的 “入库” 了吗?

这部分是测试 “注册成功后,数据库用户数量是否变化”,保证代码真的把用户数据存到数据库啦~

def test_register_post(user, client, data, code, msg):  # 1. 发请求前,先查数据库用户数量  user_list = list(User.objects.all())  user_count = len(user_list)  assert user_count == 1  # 假设测试前只有 1 个用户(图 1 里的 test_user)  # 2. 发 POST 请求测试注册  resp = client.post(...)  # (和之前一样,发请求、解析响应)  # 3. 断言响应是否符合预期(code、msg)  assert resp_json["code"] == code  assert resp_json["msg"] == msg  # 4. 如果注册成功(code == 0),再查数据库用户数量  if code == 0:  user_list = list(User.objects.all())  user_count = len(user_list)  assert user_count == 2  # 注册成功后,应该新增 1 个用户 → 总数 2  
逐行拆解
  • user_list = list(User.objects.all()) → user_count = len(user_list)

    • 发请求,先查数据库里的所有用户,数一下有多少个(user_count)。
    • 假设测试环境里,一开始只有图 1 里创建的 test_user,所以 user_count == 1
  • assert user_count == 1

    • 确保测试前数据库状态 “干净”,只有 1 个测试用户,避免其他数据干扰测试结果~
  • if code == 0:

    • code == 0 代表 “注册成功”,这时需要再查数据库,确认用户真的新增了!
  • user_list = list(User.objects.all()) → user_count = len(user_list)

    • 发请求,再次查数据库用户数量。
  • assert user_count == 2

    • 注册成功的话,用户数量应该从 1 变成 2(原来的 test_user + 新注册的用户)。
    • 相当于:“嘿,数据库~ 注册成功后,用户是不是真的存进来啦?数量对不对呀?”

 

🌈 整体流程总结

  1. 造用户:用 @pytest.fixture 提前在数据库造一个 test_user,当 “测试种子”。
  2. 批量测注册:用 @pytest.mark.parametrize 批量测试各种注册场景(用户名空、密码错、注册成功)。
  3. 发请求:用 client.post 模拟浏览器发注册请求,看服务器咋响应。
  4. 断言响应:检查返回的 code 和 msg 是否符合预期(比如注册成功时 code=0)。
  5. 数据库校验:注册成功后,再查数据库用户数量,确保真的新增了用户~

user 固件也就是说意义在于数据库的初始化,管理 验证是否用户名重复

1. 🛠️ 帮数据库 “热热身”

user fixture 里的 User.objects.create_user(...) 一执行,就像给数据库发了条消息:“喂~ 准备好啦,要开始测试咯!”
Django 会因此自动完成数据库连接、创建测试表等一系列准备工作,避免测试时出现 “数据库还没启动” 的尴尬错误~ 就像玩游戏前先加载地图,不然点 “开始” 会卡住呀!

 

2. 🆚 提供 “参照物” 方便验证

比如测试 “用户名不能重复” 时,user 就像一个 “标杆用户”🆔:

  • 它的用户名是 test_user,已经存在于数据库里(先建一个user数据,所以后面的断言是1->2)
  • 当你用同样的用户名 test_user 去注册时,就能验证系统会不会报错 “用户名已存在”
  • 如果没有这个 “参照物”,数据库空空如也,根本测不出 “重复注册” 的逻辑对不对呀~

所以哪怕 user 没在代码里被直接 “点名”,它也是测试里的 “幕后功臣”🌟:既让数据库准备好工作,又提供了关键的 “对比数据”,保证各种注册场景都能被准确测试到~

 

Django 测试中通过 user fixture 自动完成数据库连接的过程

 

🌱 第一步:pytest-django 的 “数据库开关”

user fixture 里有 User.objects.create_user(...) 这行代码 —— 它要往数据库里写数据,这就像给 pytest-django 递了一张 “需要数据库” 的门票🎫。

pytest-django 看到这张 “门票” 后,会自动触发一个核心机制:启用数据库连接
(如果测试里完全用不到数据库操作,pytest-django 会默认 “关闭” 数据库,让测试跑得更快~)

 

🛠️ 第二步:创建 “临时测试数据库”

为了不污染你的真实数据库(比如开发环境的 db.sqlite3),pytest-django 会偷偷做一件事:
自动创建一个全新的临时数据库(名字通常是 test_你的数据库名,比如 test_myproject)。

这个临时数据库就像一个 “一次性舞台”:

  • 结构和你的真实数据库一模一样(表、字段都照着抄)
  • 但里面的数据是干净的,专门给测试用
  • 测试结束后会自动删除,不会留下任何痕迹~

 

🔗 第三步:自动连接到临时数据库

Django 的核心配置里有 DATABASES 选项(在 settings.py 里),比如:

DATABASES = {  'default': {  'ENGINE': 'django.db.backends.sqlite3',  'NAME': BASE_DIR / 'db.sqlite3',  # 真实数据库  }  
}  

当 pytest-django 检测到需要数据库时,会自动 “替换” 这个配置:
把 NAME 改成临时数据库的路径(比如 test_db.sqlite3),然后调用 Django 内置的 connection 模块,建立和这个临时数据库的连接。

这一步就像:
你本来要去 “正式餐厅”(真实数据库),但测试时被悄悄引导到了 “隔壁的临时分店”(临时数据库),地址变了,但进门的方式(连接方式)完全一样~

 

🧹 第四步:自动执行数据库迁移

连接好临时数据库后,pytest-django 还会自动做一件事:
运行所有 migrations(数据迁移文件),确保临时数据库的表结构和你的项目代码完全同步。

就像:
临时舞台搭好了,但还得按设计图(migrations 文件)摆好桌椅(数据表),演员(测试数据)才能上场~

 

🌟 总结:user fixture 触发的 “全自动流程”

  1. user fixture 里的 User.objects.create_user() 触发 “需要数据库” 的信号
  2. pytest-django 接收到信号,创建临时数据库
  3. 自动修改数据库配置,连接到临时数据库
  4. 自动运行迁移,确保表结构正确
  5. 执行 create_user,往临时数据库里写入测试用户数据

整个过程完全自动,不需要你手动写 connect() 或 create_database() 之类的代码~ 就像点外卖时,平台自动帮你完成 “找餐厅、下单、配送”,你只需要等着吃(写测试)就行啦! 😋

 

为什么resp = client.post(...) 就能把提交的data放到数据库里呢

🌠 第一步:client.post(...) 是 “发件人”

🛣️ 第二步:Django 路由 “指路”

🏭 第三步:视图函数 “处理包裹”

register_view 视图函数会打开包裹(data),做一系列操作:

  1. 检查数据:比如看看用户名是不是空的、密码够不够长(就像快递员检查包裹是否违禁品)
  2. 创建用户:如果数据没问题,就会调用 User.objects.create_user(...)(Django 的 ORM 方法),把 data 里的用户名、密码等信息 “翻译” 成数据库能懂的语言(SQL 语句)。

📦 第四步:ORM 当 “翻译官”,把数据存进数据库

Django 的 ORM(对象关系映射)是个超厉害的 “翻译官”:

  • 你写的 User.objects.create_user(username="小明"),它会自动翻译成 SQL 语句:
    INSERT INTO auth_user (username, password) VALUES ('小明', '加密后的密码')
  • 然后通过之前 user fixture 已经建好的数据库连接,把这条 SQL 发给临时数据库执行。

数据库收到指令后,就会在 auth_user 表(用户表)里新增一行记录 —— 你的 data 就这样成功 “住进” 数据库啦!

 

那没有user这个固件了 直接resp = client.post(...) 能不能连接上数据库

哈哈,这个问题就像在问:“如果没有提前打开厨房门,能直接炒菜吗?” 答案是 ——大部分情况会失败!用超萌的例子解释:

假设 user fixture 是 “提前打开厨房门的人”👩🍳,而 client.post(...) 是 “炒菜的动作”:

 

🍳 情况 1:如果你的 post 请求需要操作数据库(比如注册用户)

  • 这时候必须先有 “可用的厨房”(数据库连接),否则炒不了菜~
  • 没有 user fixture,pytest-django 可能会 “偷懒” 不初始化数据库(因为没收到 “需要数据库” 的信号)。
  • 当 client.post(...) 触发视图函数去写数据库时,会发现 “厨房门没开”(数据库没连接),直接报错:DatabaseError 或 “表不存在” 之类的错误~

就像没开煤气就想炒菜,锅是冷的,根本炒不熟呀!

 

🍳 情况 2:如果你的 post 请求完全不碰数据库(比如只是返回固定文本)

  • 比如视图函数只是返回 {"code":0},不读写数据库,那就算没有 user fixture,client.post(...) 也能正常运行~
  • 这就像 “凉拌菜”,不需要开火(数据库),直接拌一拌就能吃~

 

📌 关键结论

client.post(...) 本身不会主动触发数据库连接,它只是发请求的 “快递员”。
是否能连接数据库,取决于测试中有没有其他触发数据库初始化的操作(比如 user fixture 里的 User.objects.create_user)。

如果你的 post 请求需要操作数据库,最好还是带上 user fixture 或其他 “数据库触发器”(比如 django_db fixture),确保 “厨房门是开着的” 哦~ 🔑

 

可是resp = client.post(...) 的后端视图处理不是有创建User.objects.create_user的代码吗 为啥不能执行到这里的时候开始初始化数据库

这个问题超棒!就像在问:“既然炒菜时会用到煤气,为啥不能在炒菜时再开煤气呢?” 这里藏着一个 “先后顺序” 的小秘密~ 

 

🔥 核心原因:“煤气”(数据库连接)必须在 “点火”(执行代码)前打开

Django 测试中,数据库初始化(连接、创建临时表等)是 **“提前准备工作”,必须在任何数据库操作代码(比如 User.objects.create_user)执行之前 ** 完成。

而 client.post(...) 触发的视图函数里的 create_user,属于 “测试过程中才会执行的代码”—— 就像 “点火炒菜” 这个动作,必须在 “煤气已经打开” 之后才能进行。

 

🍳 具体流程对比:

有 user fixture 时:
  1. 测试开始前user fixture 先执行 User.objects.create_user → 触发数据库初始化(开煤气)
  2. 测试进行中client.post(...) 触发视图函数 → 执行 create_user(点火炒菜)→ 顺利执行(煤气已开)
没有 user fixture 时:
  1. 测试开始前:没有任何数据库操作 → pytest-django 认为 “不需要数据库”→ 不初始化(没开煤气)
  2. 测试进行中client.post(...) 触发视图函数 → 执行 create_user(想点火炒菜)→ 发现 “煤气没开”(数据库未初始化)→ 报错!

 

🛠️ pytest-django 的 “小规矩”:

pytest-django 有个 “懒加载” 原则:只有在测试开始前,检测到 “明确需要数据库” 的信号(比如 fixture 里有数据库操作),才会提前初始化数据库

而视图函数里的 create_user 是 “测试运行到一半才执行的代码”,这时候再想初始化数据库已经太晚了 —— 就像炒菜炒到一半才发现没开煤气,火根本点不着呀!

 

🌟 解决办法:给测试 “明确开煤气”

如果不想用 user fixture,也可以用 pytest-django 提供的 django_db fixture,直接告诉 pytest:“我要用到数据库,麻烦提前准备好哦~”

# 不用 user fixture,改用 django_db  fixture  
def test_register_post(client, django_db, data, code, msg):  resp = client.post("/accounts/register", data=data)  # 此时数据库已提前初始化  # ... 后续断言 ...  

django_db 就像一个 “备用煤气开关”,专门负责在测试前打开数据库连接~

可是不做单元测试 我只做开发的话视图User.objects.create_user前也没有什么启动数据库的操作啊

在实际开发中,情况和测试环境完全不同!就像 “家里做饭” 和 “野外露营做饭” 的区别 —— 家里的煤气灶是长期打开、随时可用的,而露营时需要提前手动生火~

 

🏠 开发环境中,数据库连接是 “自动提前就绪” 的

在开发阶段,当你启动 Django 项目(比如 python manage.py runserver)时,Django 会自动完成一系列 “初始化工作”,其中就包括数据库连接的建立,根本不需要你手动写代码 “启动数据库”。

具体来说,Django 会:

  1. 读取 settings.py 里的数据库配置(DATABASES 字典),比如数据库类型(MySQL/PostgreSQL)、地址、用户名密码等。
  2. 启动时自动创建数据库连接池(保持和数据库的 “长期通话” 状态)。
  3. 检查数据库表结构(如果用了 migrate 迁移),确保表已经存在。

这就像你家里的煤气灶,只要插上电、打开总开关,随时拧旋钮就能点火 ——数据库连接在项目启动时就已经 “待命” 了

 

🚀 所以视图里的 User.objects.create_user 能直接执行

当用户在浏览器里提交表单(或者你用 Postman 发请求),触发视图函数里的 User.objects.create_user 时:

  • Django 已经通过启动时建立的数据库连接,把 ORM 代码(create_user)翻译成 SQL。
  • 直接通过现成的连接发给数据库执行,根本不需要 “临时启动数据库”。

这和测试环境的区别在于:

  • 开发环境:项目启动时强制初始化数据库连接(因为 runserver 命令默认需要数据库支持)。
  • 测试环境(pytest):为了效率,默认不初始化数据库,只有明确告诉它 “要用数据库”(比如用 django_db fixture),才会提前准备。

 

📌 总结:开发时 “无需手动启动” 的核心原因

Django 的 runserver 命令在启动项目时,会自动根据 settings.py 的配置完成数据库连接初始化,相当于 “项目一启动,数据库就处于‘开机待命’状态”。

http://www.lryc.cn/news/609145.html

相关文章:

  • Day23--回溯--39. 组合总和,40. 组合总和 II,131. 分割回文串
  • 机器人抓取流程介绍与实现——机器人抓取系统基础系列(七)
  • 深度学习中卷积与互相关
  • [ java Thread 线程 ] 由“一对一“到“一对多“
  • 【Linux网络编程基础--socket地址API】
  • 使用 Vuepress + GitHub Pages 搭建项目文档
  • GraphRAG:基于知识图谱的检索增强生成技术解析
  • 微分方程模型:用“变化率”的语言,描绘世间万物的动态演化
  • async/await和Promise之间的关系是什么?(补充)
  • 图像识别区分指定物品与其他物体
  • SelectDB数据库,新一代实时数据仓库的全面解析与应用
  • Kubernetes滚动更新、蓝绿部署与金丝雀发布方案对比分析及选型建议
  • 延迟任务方案-DelayQueue
  • noob靶机复现笔记
  • 【stm32】GPIO输入
  • 环绕字符串中的唯一子字符串-动态规划
  • 其它IO函数
  • STM32 串口发送
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘scikit-learn’问题
  • Linux环境下使用Docker搭建多服务环境
  • 学习游戏制作记录(实现克隆攻击的克隆复制和水晶代替克隆)8.3
  • 【gradle】插件那些事
  • 7.28-8.3周报
  • C的数据类型与变量
  • 高质量数据集|从武汉光谷《面向科技情报大模型的高质量数据集建设》招标项目谈起
  • Matlab算法编程示例4:数值解法求解常微分方程的代码实例
  • deep research|从搜索引擎到搜索助手的实践(一)
  • Android 之 MVC架构
  • TVS二极管数据手册解读
  • kraft的设计与实现