简体   繁体   English

APITestCase Django中的模型冲突

[英]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.

相关问题 Pytest Django function mocking APITestCase - Pytest Django function mocking APITestCase Django 覆盖范围不包括 APITestCase 测试 - Django coverage doesn't cover APITestCase tests django rest 框架 APITestCase 包括文件字段 - django rest framework APITestCase including file fields 如何基于 Django 中的 APITestCase 正确运行测试? - How to properly run tests based on the APITestCase in Django? 使用API​​TestCase和django-rest-framework - using APITestCase with django-rest-framework 与Django模型中的后保存和__unicode __(self)冲突 - conflict with post save and __unicode__(self) in django models 在 django 模型中使用协议会引发元类冲突错误 - Using Protocols in django models raises metaclass conflict error 使用 ABC、PolymorphicModel、django-models 产生元类冲突 - Using ABC, PolymorphicModel, django-models gives metaclass conflict Django Rest Framework中APITestCase,APISImpleTestCase和APITransactionTestCase的不同用例有哪些? - What are the different use cases for APITestCase, APISImpleTestCase, and APITransactionTestCase in Django Rest Framework Django rest APITestCase 客户端在单元测试中将 null 布尔值转换为 false - Django rest APITestCase client converts null booleans into false in unit tests
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM