如何为全外键字段的Django模型编写DRF测试用例?
问题描述
我有一个所有字段均为其他模型外键的Example模型,代码如下:
class Example(models.Model): package = models.ForeignKey( Destination, related_name="packages", on_delete=models.CASCADE ) user = models.ForeignKey( User, on_delete=models.CASCADE, null=True, related_name="user_packages", ) tour = models.ForeignKey( Tours, on_delete=models.CASCADE, null=True, related_name="tour_packages", )
我已知单外键模型的测试用例编写方式(示例如下):
class NewsLetter(models.Model): NewsLetterID = models.AutoField(primary_key=True) Email = models.CharField(max_length=255) Connected = models.BooleanField(default=False) UserID = models.ForeignKey(User, on_delete=models.CASCADE) class Meta: db_table = 'NewsLetter' @classmethod def setUpTestData(cls): #Set up non-modified objects used by all test methods user = User.objects.create(<fill params here>) NewsLetter.objects.create(NewsLetterID=1, Email='test@test.com', Connected=False,UserID=user)
但我的Example模型中,关联模型(如Destination)本身也包含外键字段,请问如何为该模型的创建API编写Django REST Framework单元测试?
解决方案
核心思路是从最底层的依赖模型开始创建实例,逐层往上构建所有关联对象,再测试API的创建逻辑。以下是具体实现步骤:
1. 构建所有依赖模型实例
假设Destination模型依赖于另一个Country模型(可根据你的实际依赖替换),首先需要在测试的setUpTestData中创建最底层的依赖,再依次创建Destination、User、Tours,最后才能生成API测试所需的请求数据或直接创建Example实例。
示例依赖模型(替换为你的实际模型结构即可):
class Country(models.Model): name = models.CharField(max_length=100) class Destination(models.Model): name = models.CharField(max_length=100) country = models.ForeignKey(Country, on_delete=models.CASCADE) class Tours(models.Model): name = models.CharField(max_length=100)
2. 编写DRF单元测试用例
使用Django的TestCase和DRF的APIClient来测试创建API,具体代码如下:
from django.test import TestCase from django.urls import reverse from rest_framework.test import APIClient from rest_framework import status from django.contrib.auth.models import User from .models import Country, Destination, Tours, Example class ExampleAPITestCase(TestCase): @classmethod def setUpTestData(cls): # 创建最底层依赖:Country cls.country = Country.objects.create(name="Test Country") # 创建Destination(依赖Country) cls.destination = Destination.objects.create(name="Test Destination", country=cls.country) # 创建User cls.user = User.objects.create_user(username="testuser", password="testpass123") # 创建Tours cls.tour = Tours.objects.create(name="Test Tour") # 初始化API客户端 cls.client = APIClient() # 登录客户端(如果API需要认证) cls.client.login(username="testuser", password="testpass123") def test_create_example_api(self): # 构造POST请求的数据,外键字段传入对应模型的主键ID url = reverse("example-list") # 替换为你的Example API的列表/创建路由名称 data = { "package": self.destination.id, "user": self.user.id, "tour": self.tour.id } # 发送POST请求 response = self.client.post(url, data, format="json") # 断言请求成功 self.assertEqual(response.status_code, status.HTTP_201_CREATED) # 断言数据库中创建了对应的Example记录 self.assertEqual(Example.objects.count(), 1) created_example = Example.objects.first() self.assertEqual(created_example.package, self.destination) self.assertEqual(created_example.user, self.user) self.assertEqual(created_example.tour, self.tour) def test_create_example_with_null_fields(self): # 测试允许为null的外键字段不传值的情况 url = reverse("example-list") data = { "package": self.destination.id # 不传user和tour,因为它们允许为null } response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) created_example = Example.objects.first() self.assertIsNone(created_example.user) self.assertIsNone(created_example.tour)
关键注意事项
- 依赖顺序:必须先创建被外键引用的模型实例,比如先创建
Country才能创建Destination,否则会抛出数据库外键约束错误。 - API路由:确保
reverse("example-list")中的名称与你的urls.py中定义的API路由名称一致。 - 认证:如果你的API需要登录才能创建,记得在
setUpTestData中登录客户端;如果不需要认证,可以去掉登录代码。 - 字段验证:如果序列化器有额外的验证逻辑,需要补充对应测试用例,比如测试传入不存在的外键ID时是否返回400错误。
内容的提问来源于stack exchange,提问作者Reactoo
相关产品推荐
相关产品推荐

