Django测试
一、今日学习内容概述
学习模块 | 重要程度 | 主要内容 |
---|---|---|
测试基础 | ⭐⭐⭐⭐⭐ | TestCase、断言方法 |
模型测试 | ⭐⭐⭐⭐⭐ | 模型方法、数据验证 |
视图测试 | ⭐⭐⭐⭐ | 请求处理、响应验证 |
表单测试 | ⭐⭐⭐⭐ | 表单验证、数据处理 |
覆盖率测试 | ⭐⭐⭐⭐ | coverage配置、报告生成 |
二、测试基础示例
2.1 基本测试类
# tests/test_base.py
from django.test import TestCase
from django.contrib.auth.models import User
from .models import Article
class ArticleTestCase(TestCase):
def setUp(self):
"""测试前准备工作"""
# 创建测试用户
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
# 创建测试文章
self.article = Article.objects.create(
title='Test Article',
content='Test Content',
author=self.user
)
def tearDown(self):
"""测试后清理工作"""
self.user.delete()
self.article.delete()
def test_article_creation(self):
"""测试文章创建"""
self.assertEqual(self.article.title, 'Test Article')
self.assertEqual(self.article.author, self.user)
def test_article_str_representation(self):
"""测试文章字符串表示"""
self.assertEqual(str(self.article), 'Test Article')
2.2 测试工具类
# tests/test_utils.py
import unittest
from django.test import TestCase
from .utils import calculate_reading_time, generate_slug
class UtilsTestCase(TestCase):
def test_reading_time_calculation(self):
"""测试阅读时间计算"""
# 准备测试数据
content = ' '.join(['word'] * 500) # 500个单词
# 调用测试函数
reading_time = calculate_reading_time(content)
# 验证结果(假设每分钟阅读200个单词)
self.assertEqual(reading_time, 2.5)
def test_slug_generation(self):
"""测试slug生成"""
test_cases = [
('Hello World', 'hello-world'),
('测试文章', 'ce-shi-wen-zhang'),
('Python & Django', 'python-django'),
]
for title, expected_slug in test_cases:
with self.subTest(title=title):
self.assertEqual(generate_slug(title), expected_slug)
三、模型测试示例
# tests/test_models.py
from django.test import TestCase
from django.core.exceptions import ValidationError
from django.utils import timezone
from .models import Article, Category
class ArticleModelTest(TestCase):
@classmethod
def setUpTestData(cls):
"""创建测试数据"""
cls.category = Category.objects.create(name='Test Category')
cls.article = Article.objects.create(
title='Test Article',
content='Test Content',
category=cls.category,
status='draft'
)
def test_title_max_length(self):
"""测试标题长度限制"""
article = Article.objects.get(id=self.article.id)
max_length = article._meta.get_field('title').max_length
self.assertEqual(max_length, 200)
def test_article_label(self):
"""测试字段标签"""
article = Article.objects.get(id=self.article.id)
title_label = article._meta.get_field('title').verbose_name
self.assertEqual(title_label, '标题')
def test_publish_article(self):
"""测试文章发布功能"""
article = Article.objects.get(id=self.article.id)
article.publish()
self.assertEqual(article.status, 'published')
self.assertIsNotNone(article.published_at)
def test_article_ordering(self):
"""测试文章排序"""
Article.objects.create(
title='Second Article',
content='Content',
category=self.category,
status='published'
)
articles = Article.objects.all()
self.assertEqual(articles[0].title, 'Second Article')
四、视图测试示例
# tests/test_views.py
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth.models import User
from .models import Article
class ArticleViewsTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.article = Article.objects.create(
title='Test Article',
content='Test Content',
author=self.user
)
def test_article_list_view(self):
"""测试文章列表视图"""
response = self.client.get(reverse('article_list'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'articles/article_list.html')
self.assertContains(response, 'Test Article')
def test_article_detail_view(self):
"""测试文章详情视图"""
response = self.client.get(
reverse('article_detail', args=[self.article.id])
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'articles/article_detail.html')
self.assertEqual(response.context['article'], self.article)
def test_create_article_view(self):
"""测试创建文章视图"""
self.client.login(username='testuser', password='testpass123')
response = self.client.post(reverse('article_create'), {
'title': 'New Article',
'content': 'New Content',
})
self.assertEqual(response.status_code, 302) # 重定向
self.assertTrue(Article.objects.filter(title='New Article').exists())
五、表单测试示例
# tests/test_forms.py
from django.test import TestCase
from .forms import ArticleForm, CommentForm
class ArticleFormTest(TestCase):
def test_article_form_valid_data(self):
"""测试表单有效数据"""
form = ArticleForm(data={
'title': 'Test Article',
'content': 'Test Content',
'category': 1,
'status': 'draft'
})
self.assertTrue(form.is_valid())
def test_article_form_invalid_data(self):
"""测试表单无效数据"""
form = ArticleForm(data={})
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 3) # title, content, category 必填
def test_article_form_title_max_length(self):
"""测试标题长度限制"""
form = ArticleForm(data={
'title': 'x' * 201, # 超过最大长度
'content': 'Test Content',
'category': 1
})
self.assertFalse(form.is_valid())
self.assertIn('title', form.errors)
六、覆盖率测试配置
6.1 安装配置
# 安装coverage
pip install coverage
# 运行测试并收集覆盖率数据
coverage run manage.py test
# 生成覆盖率报告
coverage report
coverage html
6.2 配置文件
# .coveragerc
[run]
source = .
omit =
*/migrations/*
*/tests/*
*/venv/*
manage.py
[report]
exclude_lines =
pragma: no cover
def __repr__
raise NotImplementedError
if settings.DEBUG
pass
七、测试流程图
八、高级测试技巧
8.1 异步测试
from django.test import TestCase
import asyncio
class AsyncTests(TestCase):
async def test_async_view(self):
"""测试异步视图"""
response = await self.async_client.get('/async-view/')
self.assertEqual(response.status_code, 200)
async def test_async_task(self):
"""测试异步任务"""
result = await async_task()
self.assertTrue(result)
8.2 测试fixtures
# tests/fixtures/test_data.json
[
{
"model": "app.category",
"pk": 1,
"fields": {
"name": "Test Category",
"description": "Test Description"
}
}
]
# tests/test_with_fixtures.py
class CategoryTestCase(TestCase):
fixtures = ['test_data.json']
def test_category_exists(self):
"""测试fixture数据加载"""
category = Category.objects.get(pk=1)
self.assertEqual(category.name, 'Test Category')
九、测试最佳实践
测试命名规范
- 文件名以test_开头
- 测试类以Test结尾
- 测试方法以test_开头
测试组织结构
- 按功能模块分组
- 保持测试独立性
- 避免测试间依赖
测试用例设计
- 包含边界条件
- 考虑异常情况
- 验证业务规则
性能优化
- 使用setUpTestData
- 合理使用事务
- 避免不必要的数据库操作
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!