每天40分玩转Django:Django测试

发布于:2024-12-23 ⋅ 阅读:(16) ⋅ 点赞:(0)

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')

九、测试最佳实践

  1. 测试命名规范

    • 文件名以test_开头
    • 测试类以Test结尾
    • 测试方法以test_开头
  2. 测试组织结构

    • 按功能模块分组
    • 保持测试独立性
    • 避免测试间依赖
  3. 测试用例设计

    • 包含边界条件
    • 考虑异常情况
    • 验证业务规则
  4. 性能优化

    • 使用setUpTestData
    • 合理使用事务
    • 避免不必要的数据库操作

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!