[英]Models conflict in APITestCase Django
I'm trying to setup tests for my DRF API. 我正在尝试为我的DRF API设置测试。 I've got 2 models:
我有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'
)
I've decided to separate setUp methods for every test file, so my 2 test files look like this: 我决定为每个测试文件分离setUp方法,所以我的2个测试文件看起来像这样:
test_projects.py 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())
and test_users.py 和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)
When I run these 2 tests separately like ./manage.py test core.tests.test_users
and ./manage.py test core.tests.test_projects
they work as expected, without any erros. 当我单独运行这两个测试时,比如
./manage.py test core.tests.test_users
和./manage.py test core.tests.test_projects
它们按预期工作,没有任何错误。
But when I run ./manage.py test core.tests
it fails on test_user_not_created_twice() and test_user_created_for_specific_project() with: 但是当我运行
./manage.py test core.tests
它在test_user_not_created_twice()和test_user_created_for_specific_project()上失败:
Project matching query does not exist
项目匹配查询不存在
What's wrong? 怎么了? I've even added Project.objects.all().delete() in tearDownClass methods, this didn't help.
我甚至在tearDownClass方法中添加了Project.objects.all()。delete(),这没有帮助。 And also, is it a good practice to specify setUpClass methods like this?
而且,指定这样的setUpClass方法是一个好习惯吗?
I couldn't test this since some of the models in the tests weren't in the example, but I think it's likely that it's the hard-coded Project pk that you are referencing that is causing the issue - when running both test cases the first one will set up and then delete Project with pk #1 and then the second will set up and delete Project with pk #2 我无法测试这个,因为测试中的某些模型不在示例中,但我认为这可能是您引用的硬编码的Project pk导致问题 - 当运行两个测试用例时第一个将设置然后删除带有pk#1的项目,然后第二个将设置并删除带有pk#2的项目
You could fix this by setting reset_sequences = True
on your APITestCase
classes, and then the second test case would be creating a Project with pk #1 again. 您可以通过在
APITestCase
类上设置reset_sequences = True
来解决此问题,然后第二个测试用例将再次创建一个带有pk#1的项目。
https://docs.djangoproject.com/en/2.2/topics/testing/advanced/#django.test.TransactionTestCase.reset_sequences https://docs.djangoproject.com/en/2.2/topics/testing/advanced/#django.test.TransactionTestCase.reset_sequences
The better practice would be to hold on to a reference to your Project
by doing self.project = Project.objects.create(name="Test Project")
in your test setup code, and then using self.project.pk
rather than hardcoding pk 1
in your test cases. 更好的做法是通过在测试设置代码中执行
self.project = Project.objects.create(name="Test Project")
,然后使用self.project.pk
而不是硬编码来保持对Project
的引用。 pk 1
在您的测试用例中。
Also you should consider using setUpTestData rather than setUpClass
for your use case (here's the source code for both ). 此外,您应该考虑使用setUpTestData而不是
setUpClass
作为您的用例(这里是两者的源代码 )。 No teardown should be required for data you add to the database in any setup as this all happens within a transaction that gets reverted after all test cases in the class have run. 在任何设置中添加到数据库的数据都不需要拆卸,因为这一切都发生在一个事务中,该事务在类中的所有测试用例运行后都会被恢复。
It is assumed that individual tests read this class-level state, but don't modify it, so your test test_project_deletion()
could cause other tests to fail since it deletes an object created at the class level. 假设各个测试读取此类级别状态,但不修改它,因此测试
test_project_deletion()
可能导致其他测试失败,因为它删除了在类级别创建的对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.