【问题标题】:Django rest framework: unit testing database issueDjango rest框架:单元测试数据库问题
【发布时间】:2017-02-13 11:25:13
【问题描述】:

我正在对其余的 API 进行单元测试。我正在使用 django 休息框架。 APIs 正在将数据保存到数据库中并从数据库中获取数据。这两个操作都不起作用,或者如果它起作用,我无法在数据库中看到它。 APIs 也使用 django-fsm,因此我需要来自 db 的相同数据用于其他测试。由于 django-fsm,测试依赖于之前的测试。 api总是会改变状态。但是现在我在测试运行期间无法看到数据库中的任何数据。不知道数据保存在哪里,在哪个数据库中。

以下是我的测试设置:-

DATABASES = {
'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': join(PROJECT_ROOT, 'run', 'db_for_testing.sqlite3'),
    'TEST': {
        'NAME': 'test_db_for_testing',
    },
 },
}

下面是我的api:-

class DummyView(CreateAPIView):
    def post(self, request, *args, **kwargs):
        data = request.data.copy()
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        order = self.model(book=serializer.data.get('book'))
        order.save()
        data = {
        'status_code': 200,
        'message': 'successfully.'
        }
        return Response(data, status=status.HTTP_200_OK)

由于我的测试依赖于之前将数据保存到数据库的测试,因此其他测试也失败了。我正在使用 rest_framework 的 APITestCase。 帮助伙计们。 提前致谢。

【问题讨论】:

    标签: django unit-testing django-rest-framework fsm web-api-testing


    【解决方案1】:

    如果我正确理解您的问题,Django 在每次测试后“清除”数据库(回滚或截断)。因此您需要相应地编写测试。

    见:https://docs.djangoproject.com/en/1.10/topics/testing/tools/#transactiontestcase

    【讨论】:

    • 那么我将如何强制它在每次测试后不清除数据库?因为每次测试我都需要相同的数据。因为有限状态机
    • 把它们写在一个测试中?
    • 好的。谢谢。但是必须有一种方法可以强制它不截断数据库。
    • per stackoverflow.com/questions/30504031/… 我认为你可以使用 Python 的 unittest.TestCase
    【解决方案2】:

    TL;DR - 解决方案:使用 SimpleTestCase - 参见下面的示例


    说明

    问题是 Django 为涉及数据库查询的测试提供的推荐测试类 TransactionTestCase 和子类 TestCase 将每个测试包装在事务中以加快每次测试后重置数据库的过程。来源:Django TransactionTestCase docs

    可以通过使用作为 TransactionTestCase 的父类的 SimpleTestCase 来避免这种行为。然后,您必须通过将 allow_database_queries 设置为 True 来明确指定您希望允许数据库查询-

    另外请注意,您需要负责在测试后进行的任何清洁工作。您可以通过覆盖 tearDownClass 类方法来做到这一点。同样,在运行测试之前,任何初始化都有一个 setUpClass 类方法。记得调用超级方法。 See full details in the docs


    示例

    from django.test import SimpleTestCase
    
    class MyTestCase(SimpleTestCase):
        allow_database_queries = True
    
        @classmethod
        def setUpClass(cls):
            # Do your pre test initialization here.
            super(MyTestCase, cls).setUpClass()
    
        @classmethod
        def tearDownClass(cls):
            # Do your post test clean uphere.
            super(MyTestCase, cls).tearDownClass()
    
        def test_add_data_through_api(self):
            # Add 'dat data
            ...
    
        def test_work_with_data_from_previous_test(self):
            # Work 'dat data
            ...
    

    【讨论】:

      【解决方案3】:

      在调用 manage.py 测试时使用 --keepdb 选项:

      https://docs.djangoproject.com/en/2.1/ref/django-admin/#cmdoption-test-keepdb

      它从 django 1.8 开始可用。 希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        这里我的三个测试依赖于前一个。如果我将它们作为单独的测试运行,则先前的测试数据会被删除,而后来的测试数据因丢失该数据而失败。

        所以我把它们放在两个不同的函数中,都不是测试函数。最后从一个测试函数中调用依赖函数。

        class test_profile(APITestCase):
            def setUp(self):
                super_user = default_service.create_super_user()
                self.application = default_service.create_oath2_application(super_user.id)
                self.content_type = "application/x-www-form-urlencoded"
                self.username = "user@domain.com"
                self.password = "pass123"
        
            def create_profile(self):
                url = reverse('EmailSignUp')
                body = {
                    "email": self.username,
                    "password": self.password,
                    "fullname": "Mamun Hasan"
                }
                response = self.client.post(url, body, CONTENT_TYPE=self.content_type)
                self.assertEqual(response.status_code, status.HTTP_200_OK)
                data = (json.loads(response.content))['data']
                # print("Profile", data)
                self.assertEqual(data['username'], self.username)
                self.assertEqual(data['fullname'], data['fullname'])
        
            def get_access_token(self):
                url = reverse('oauth2_provider:token')
                body = {
                    "username": self.username,
                    "password": self.password,
                    "grant_type": self.application.authorization_grant_type,
                    "client_id": self.application.client_id,
                    "client_secret": self.application.client_secret,
                }
                response = self.client.post(url, body, CONTENT_TYPE=self.content_type)
                self.assertEqual(response.status_code, status.HTTP_200_OK)
                data = (json.loads(response.content))
                # print("AccessToken", data)
                self.assertEqual(data['token_type'], 'Bearer')
                return data
        
            def get_profile(self, oath2_token):
                url = reverse('GetProfile')
                authorization = oath2_token["token_type"] + ' ' + oath2_token["access_token"]
                response = self.client.get(url, CONTENT_TYPE=self.content_type, HTTP_AUTHORIZATION=authorization)
                self.assertEqual(response.status_code, status.HTTP_200_OK)
                data = (json.loads(response.content))['data']
                # print("Profile", data)
                self.assertEqual(data['username'], self.username)
        
            def test_dependent(self):
                self.create_profile()
                oath2_token = self.get_access_token()
                self.get_profile(oath2_token)
        

        我没有找到任何解决方案来提交以前的 API 数据。如果有人知道请发表评论。所以我这样做了。我不知道这是最好的解决方案,但它可以工作并经过测试。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-19
          • 2016-05-29
          • 2015-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-01
          相关资源
          最近更新 更多