我正在尝试为DRF API设置测试。我有2个型号:
class Project(models.Model):
name = models.CharField(max_length=300, unique=True)
description = models.CharField(
max_length=2000,
blank=True,
null=True,
default=None
)
created_at = models.DateTimeField(auto_now_add=True)
class TemporaryUser(models.Model):
username = models.CharField(max_length=400)
hash_id = models.URLField(default=_generate_unique_hash, unique=True)
project = models.ForeignKey(
Project,
on_delete=models.CASCADE,
related_name='users'
)
我决定为每个测试文件单独设置setUp方法,所以我的2个测试文件如下所示:
test_projects.py
class ProjectViewsTest(APITestCase):
client = APIClient()
@classmethod
def setUpClass(cls):
project = Project.objects.create(name="Test Project")
cls.project_creation_date = datetime.now().strftime(
'%Y-%m-%d %H:%M:%S'
)
Project.objects.create(
name="Test Project #2", description='Testing Project Number 2'
)
session = QuestionSession.objects.create(project=project)
cls.session_creation_date = datetime.now().strftime(
'%Y-%m-%d %H:%M:%S'
)
Question.objects.create(
description='Test Question #1',
question_type='TEXT',
answers_to_close=50,
question_session=session
)
Question.objects.create(
description='Test Question #2',
question_type='TEXT',
answers_to_close=50,
question_session=session
)
Question.objects.create(
description='Test Question #3',
question_type='TEXT',
answers_to_close=50,
question_session=session
)
@classmethod
def tearDownClass(cls):
Project.objects.all().delete()
def test_projects_fetched_with_sessions_number(self):
"""Test multiple projects are fetched with sessions counter"""
self.maxDiff = None
response = self.client.get(
'http://testserver/api/projects/', format='json'
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content,
{
"projects": [
{
"id": 1,
"name": "Test Project",
"description": None,
"sessions_number": 1,
"created_at": self.project_creation_date
},
{
"id": 2,
"name": "Test Project #2",
"description": "Testing Project Number 2",
"sessions_number": 0,
"created_at": self.project_creation_date
}
]
}
)
def test_project_fetched_with_minified_sessions(self):
"""Test a project is fetched with minified sessions"""
response = self.client.get(
'http://testserver/api/projects/1/',
format='json'
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content,
{
"id": 1,
"name": "Test Project",
"created_at": self.project_creation_date,
"sessions": [
{
"id": 1,
"name": None,
"active": True,
"created_at": self.session_creation_date,
"priority": 0,
"questions_number": 3
}
]
}
)
def test_project_creation(self):
"""Test project creation with/without passing description"""
# POST with description
response_1 = self.client.post(
'http://testserver/api/projects/create/',
format='json',
data={"name": "Test Project #3", "description": "Another Project"}
)
# POST without description
response_2 = self.client.post(
'http://testserver/api/projects/create/',
format='json',
data={"name": "Test Project #4"}
)
self.assertEqual(response_1.status_code, 200)
self.assertEqual(response_2.status_code, 200)
self.assertJSONEqual(
response_1.content,
{
'status': 'SUCCESS', 'id': 3,
'message': 'Test Project #3 has been created successfully!'
}
)
self.assertJSONEqual(
response_2.content,
{
'status': 'SUCCESS', 'id': 4,
'message': 'Test Project #4 has been created successfully!'
}
)
project_1 = Project.objects.get(id=3)
project_2 = Project.objects.get(id=4)
self.assertEqual(project_1.description, "Another Project")
self.assertEqual(project_2.description, "")
def test_project_deletion(self):
"""Test API call removes Project from database"""
self.assertTrue(Project.objects.filter(id=1).exists())
response = self.client.delete(
'http://testserver/api/projects/1/delete/',
format='json'
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
response.content,
{
'status': 'SUCCESS',
'message': 'The project #1 has been deleted'
}
)
self.assertFalse(Project.objects.filter(id=1).exists())
和 test_users.py
class TemporaryUserViewsTest(APITestCase):
client = APIClient()
@classmethod
def setUpClass(cls):
project = Project.objects.create(name="Test Project")
Project.objects.create(
name="Test Project #2", description='Testing Project Number 2'
)
TemporaryUser.objects.create(
username="TestUser", project=project
)
@classmethod
def tearDownClass(cls):
Project.objects.all().delete()
def test_created_user_exists(self):
"""Tests if a user is unique per project"""
response_1 = self.client.get(
'http://testserver/api/projects/1/temp_user/TestUser/exists/',
format='json'
)
response_2 = self.client.get(
'http://testserver/api/projects/2/temp_user/TestUser/exists/',
format='json'
)
self.assertEqual(response_1.status_code, 200)
self.assertEqual(response_2.status_code, 200)
self.assertJSONEqual(response_1.content, {"exists": True})
self.assertJSONEqual(response_2.content, {"exists": False})
def test_user_created_for_specific_project(self):
"""Test if a user is created and exists for a specific project"""
response_1 = self.client.post(
'http://testserver/api/temp_user/create/',
format='json',
data={"username": "TestUser2", "project_id": 2}
)
user = TemporaryUser.objects.get(username='TestUser2')
self.assertEqual(response_1.status_code, 200)
self.assertEqual(json.loads(response_1.content)['created'], True)
self.assertEqual(len(user.hash_id), 15)
self.assertEqual(user.username, "TestUser2")
response_2 = self.client.get(
'http://testserver/api/projects/2/temp_user/TestUser2/exists/',
format='json'
)
self.assertEqual(response_2.status_code, 200)
self.assertJSONEqual(response_2.content, {"exists": True})
def test_user_not_created_twice(self):
"""Test a user can't be created twice"""
response_1 = self.client.post(
'http://testserver/api/temp_user/create/',
format='json',
data={"username": "TestUser3", "project_id": 1}
)
response_2 = self.client.post(
'http://testserver/api/temp_user/create/',
format='json',
data={"username": "TestUser3", "project_id": 1}
)
self.assertEqual(response_1.status_code, 200)
self.assertEqual(response_2.status_code, 200)
self.assertEqual(json.loads(response_1.content)['created'], True)
self.assertEqual(json.loads(response_2.content)['created'], False)
当我分别运行./manage.py test core.tests.test_users
和./manage.py test core.tests.test_projects
之类的这两个测试时,它们按预期工作,没有任何错误。
但是当我运行./manage.py test core.tests
时,它在test_user_not_created_twice()和test_user_created_for_specific_project()上失败,
项目匹配查询不存在
怎么了?我什至在tearDownClass方法中添加了Project.objects.all()。delete(),但这没有帮助。 而且,指定这样的setUpClass方法是一种好习惯吗?
答案 0 :(得分:0)
由于测试中的某些模型不在示例中,所以我无法进行测试,但是我认为很可能是您所引用的硬编码Project pk导致了问题-在同时运行这两个程序时测试用例,第一个将设置并删除带有pk#1的项目,然后第二个将设置并删除带有pk#2的项目
您可以通过在reset_sequences = True
类上设置APITestCase
来解决此问题,然后第二个测试用例将再次使用pk#1创建一个项目。
更好的做法是通过在测试设置代码中进行Project
来保留对self.project = Project.objects.create(name="Test Project")
的引用,然后使用self.project.pk
而不是对pk {{1}进行硬编码}在您的测试用例中。
对于您的用例(这里是setUpTestData),您也应该考虑使用the source code for both而不是1
。在任何设置下添加到数据库中的数据都不需要拆卸,因为这一切都发生在事务中,该事务在类中的所有测试用例运行后都将还原。
假定单个测试读取了该类级别的状态,但未对其进行修改,因此您的测试setUpClass
可能会导致其他测试失败,因为它会删除在该类级别创建的对象。