You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DjangoBlog/accounts/tests.py

296 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from django.test import Client, RequestFactory, TestCase
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from accounts.models import BlogUser
from blog.models import Article, Category
from djangoblog.utils import * # 导入项目公共工具函数(如加密、缓存操作等)
from . import utils # 导入accounts应用自定义工具函数如验证码设置/验证、邮件发送)
# 模块级注释——accounts应用的单元测试文件
# 覆盖用户核心业务流程的测试场景:用户登录、注册、邮箱验证码验证、忘记密码重置等,
# 基于Django TestCase框架通过模拟HTTP请求和数据库操作验证业务逻辑的正确性
# 确保用户相关功能稳定可用(如权限控制、数据一致性、错误处理)
# Create your tests here.
class AccountTest(TestCase):
"""
用户相关核心功能测试类继承Django TestCase
集中测试用户登录、注册、验证码验证、忘记密码等关键流程,
每个测试方法对应一个独立的业务场景,确保测试隔离性
"""
def setUp(self):
"""
测试初始化方法(每个测试方法执行前自动调用)
初始化测试所需的核心对象和测试数据,避免重复代码
"""
self.client = Client() # 模拟HTTP客户端用于发送GET/POST请求
self.factory = RequestFactory() # 请求工厂,用于构建自定义请求对象(本测试未直接使用)
# 创建普通测试用户(用于后续登录、密码重置等测试)
self.blog_user = BlogUser.objects.create_user(
username="test", # 用户名
email="admin@admin.com", # 邮箱
password="12345678" # 密码明文create_user会自动哈希存储
)
self.new_test = "xxx123--=" # 测试用新密码(用于忘记密码重置场景)
def test_validate_account(self):
"""
测试用户登录、管理员权限、文章管理访问流程
验证点:超级用户登录成功、管理员页面访问权限、文章创建后管理页访问权限
"""
# 获取当前站点域名用于URL生成本测试未直接使用
site = get_current_site().domain
# 创建超级用户(用于测试管理员权限)
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="qwer!@#$ggg") # 超级用户密码
# 根据用户名查询创建的超级用户(验证用户创建成功)
testuser = BlogUser.objects.get(username='liangliangyy1')
# 模拟超级用户登录,验证登录结果是否成功
loginresult = self.client.login(
username='liangliangyy1',
password='qwer!@#$ggg')
self.assertEqual(loginresult, True) # 断言登录成功
# 访问管理员首页验证是否有权限状态码200表示访问成功
response = self.client.get('/admin/')
self.assertEqual(response.status_code, 200)
# 创建测试分类(用于后续文章关联)
category = Category()
category.name = "categoryaaa" # 分类名称
category.creation_time = timezone.now() # 创建时间
category.last_modify_time = timezone.now() # 最后修改时间
category.save() # 保存到数据库
# 创建测试文章(关联超级用户和分类)
article = Article()
article.title = "nicetitleaaa" # 文章标题
article.body = "nicecontentaaa" # 文章内容
article.author = user # 文章作者(超级用户)
article.category = category # 文章分类
article.type = 'a' # 文章类型(推测为"article"普通文章)
article.status = 'p' # 文章状态(推测为"published"已发布)
article.save() # 保存到数据库
# 访问文章的管理员编辑页,验证超级用户是否有权限
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200) # 断言访问成功
def test_validate_register(self):
"""
测试用户注册完整流程
验证点:注册提交、用户创建、验证邮件链接访问、登录后权限升级、文章创建、退出登录、错误密码登录
"""
# 注册前检查目标邮箱是否存在预期不存在计数为0
self.assertEquals(
0, len(
BlogUser.objects.filter(
email='user123@user.com')))
# 模拟POST请求提交注册表单调用注册路由
response = self.client.post(reverse('account:register'), {
'username': 'user1233', # 注册用户名
'email': 'user123@user.com', # 注册邮箱
'password1': 'password123!q@wE#R$T', # 注册密码(符合强度要求)
'password2': 'password123!q@wE#R$T', # 密码确认(与密码一致)
})
# 注册后检查目标邮箱是否创建成功预期计数为1
self.assertEquals(
1, len(
BlogUser.objects.filter(
email='user123@user.com')))
# 获取刚注册的用户生成邮箱验证链接基于用户ID和加密签名
user = BlogUser.objects.filter(email='user123@user.com')[0]
# 生成验证签名双重SHA256加密结合SECRET_KEY和用户ID确保链接安全性
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
path = reverse('accounts:result') # 验证结果页路由
# 拼接完整的验证链接包含用户ID和签名
url = '{path}?type=validation&id={id}&sign={sign}'.format(
path=path, id=user.id, sign=sign)
# 访问验证链接验证页面是否正常响应状态码200
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
# 使用注册的用户名和密码登录
self.client.login(username='user1233', password='password123!q@wE#R$T')
# 将该用户升级为超级用户(用于测试管理员权限)
user = BlogUser.objects.filter(email='user123@user.com')[0]
user.is_superuser = True # 设为超级用户
user.is_staff = True # 允许访问admin后台
user.save() # 保存权限变更
delete_sidebar_cache() # 清除侧边栏缓存(项目自定义缓存操作,避免缓存影响测试)
# 创建测试分类(用于关联文章)
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
category.last_modify_time = timezone.now()
category.save()
# 创建测试文章(关联升级后的超级用户)
article = Article()
article.category = category
article.title = "nicetitle333"
article.body = "nicecontentttt"
article.author = user
article.type = 'a'
article.status = 'p'
article.save()
# 访问文章管理页,验证升级后的用户是否有权限
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200)
# 模拟退出登录
response = self.client.get(reverse('account:logout'))
# 断言退出登录响应状态码301/302为跳转200为成功响应均符合预期
self.assertIn(response.status_code, [301, 302, 200])
# 退出后访问文章管理页(预期无权限,可能跳转登录页)
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
# 使用错误密码尝试登录
response = self.client.post(reverse('account:login'), {
'username': 'user1233',
'password': 'password123' # 错误密码原密码为password123!q@wE#R$T
})
# 断言登录失败后的响应状态码(跳转或重新显示登录页)
self.assertIn(response.status_code, [301, 302, 200])
# 错误密码登录后访问文章管理页(预期无权限)
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
def test_verify_email_code(self):
"""
测试邮箱验证码的验证逻辑
验证点:正确邮箱+正确验证码验证成功、错误邮箱+正确验证码验证失败
"""
to_email = "admin@admin.com" # 测试邮箱已在setUp中创建关联用户
code = generate_code() # 生成随机验证码(项目工具函数)
utils.set_code(to_email, code) # 存储验证码(推测基于缓存或数据库存储)
utils.send_verify_email(to_email, code) # 发送验证邮件(模拟邮件发送流程)
# 验证:正确邮箱+正确验证码 → 预期无错误返回None
err = utils.verify("admin@admin.com", code)
self.assertEqual(err, None)
# 验证:错误邮箱+正确验证码 → 预期返回错误信息(字符串类型)
err = utils.verify("admin@123.com", code)
self.assertEqual(type(err), str)
def test_forget_password_email_code_success(self):
"""
测试获取忘记密码验证码的成功场景
验证点:输入已注册邮箱,成功获取验证码(响应"ok"
"""
# 模拟POST请求提交邮箱获取忘记密码验证码
resp = self.client.post(
path=reverse("account:forget_password_code"), # 忘记密码验证码路由
data=dict(email="admin@admin.com") # 已注册的测试邮箱
)
self.assertEqual(resp.status_code, 200) # 断言请求成功
self.assertEqual(resp.content.decode("utf-8"), "ok") # 断言响应内容为"ok"(表示验证码发送成功)
def test_forget_password_email_code_fail(self):
"""
测试获取忘记密码验证码的失败场景
验证点:无邮箱输入、邮箱格式错误,均返回"错误的邮箱"
"""
# 场景1不传入邮箱参数 → 预期返回"错误的邮箱"
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict() # 空数据(无邮箱)
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
# 场景2传入格式错误的邮箱无@后的域名后缀)→ 预期返回"错误的邮箱"
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict(email="admin@com") # 格式错误的邮箱
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
def test_forget_password_email_success(self):
"""
测试忘记密码重置成功的场景
验证点:输入正确邮箱、验证码、新密码,密码成功更新
"""
code = generate_code() # 生成验证码
utils.set_code(self.blog_user.email, code) # 绑定验证码到测试用户邮箱
# 构建重置密码表单数据
data = dict(
new_password1=self.new_test, # 新密码
new_password2=self.new_test, # 新密码确认(与新密码一致)
email=self.blog_user.email, # 测试用户邮箱
code=code, # 正确的验证码
)
# 模拟POST请求提交重置密码表单
resp = self.client.post(
path=reverse("account:forget_password"), # 忘记密码重置路由
data=data
)
self.assertEqual(resp.status_code, 302) # 断言重置成功后跳转302为重定向
# 验证密码是否真正修改成功
blog_user = BlogUser.objects.filter(
email=self.blog_user.email,
).first() # 获取测试用户
self.assertNotEqual(blog_user, None) # 断言用户存在
# 验证新密码是否匹配check_password会自动哈希比对
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
def test_forget_password_email_not_user(self):
"""
测试忘记密码重置的失败场景:输入未注册的邮箱
验证点未注册邮箱提交重置请求返回200状态码表单重新显示错误
"""
# 构建重置密码表单数据(邮箱未注册)
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email="123@123.com", # 未注册的邮箱
code="123456", # 任意验证码
)
# 模拟POST请求提交重置密码表单
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 200) # 断言返回表单页面(显示邮箱不存在错误)
def test_forget_password_email_code_error(self):
"""
测试忘记密码重置的失败场景:验证码错误
验证点输入正确邮箱、错误验证码返回200状态码表单重新显示错误
"""
code = generate_code() # 生成正确验证码并绑定到测试用户邮箱
utils.set_code(self.blog_user.email, code)
# 构建重置密码表单数据(验证码错误)
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email, # 正确邮箱
code="111111", # 错误验证码与绑定的code不一致
)
# 模拟POST请求提交重置密码表单
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 200) # 断言返回表单页面(显示验证码错误)