【问题标题】:Django Rest Framework - How to test ViewSet?Django Rest Framework - 如何测试 ViewSet?
【发布时间】:2014-05-29 03:48:51
【问题描述】:

我在测试 ViewSet 时遇到问题:

class ViewSetTest(TestCase):
    def test_view_set(self):
        factory = APIRequestFactory()
        view = CatViewSet.as_view()
        cat = Cat(name="bob")
        cat.save()

        request = factory.get(reverse('cat-detail', args=(cat.pk,)))
        response = view(request)

我正在尝试复制这里的语法:

http://www.django-rest-framework.org/api-guide/testing#forcing-authentication

但我认为他们的 AccountDetail 视图与我的 ViewSet 不同,所以我从最后一行收到此错误:

AttributeError: 'NoneType' object has no attributes 'items'

这里有正确的语法还是我混淆了概念?我的 APIClient 测试工作,但我在这里使用工厂,因为我最终想添加“request.user = some_user”。提前致谢!

哦,客户端测试工作正常:

def test_client_view(self):
    response = APIClient().get(reverse('cat-detail', args=(cat.pk,)))
    self.assertEqual(response.status_code, 200)

【问题讨论】:

  • 我试图让强制身份验证工作,根据他们的文档,但它似乎不起作用。我不使用 drf 本机身份验证令牌,而是我自己的 jwt 令牌,因此无法通过 APIRequestFactory 进行。

标签: python django django-rest-framework


【解决方案1】:

我需要通过强制身份验证来解决这个问题,最后得到了它,这是我的测试用例的样子:

from django.test import TestCase
from rest_framework.test import APIRequestFactory
from django.db.models.query import QuerySet
from rest_framework.test import force_authenticate
from django.contrib.auth.models import User

from config_app.models import Config
from config_app.apps import ConfigAppConfig
from config_app.views import ConfigViewSet

class ViewsTestCase(TestCase):
    def setUp(self):
        # Create a test instance
        self.config = Config.objects.create(
            ads='{"frequency": 1, "site_id": 1, "network_id": 1}',
            keys={}, methods={}, sections=[], web_app='{"image": 1, "label": 1, "url": 1}',
            subscriptions=[], name='test name', build='test build', version='1.0test', device='desktop',
            platform='android', client_id=None)

        # Create auth user for views using api request factory
        self.username = 'config_tester'
        self.password = 'goldenstandard'
        self.user = User.objects.create_superuser(self.username, 'test@example.com', self.password)

    def tearDown(self):
        pass

    @classmethod
    def setup_class(cls):
        """setup_class() before any methods in this class"""
        pass

    @classmethod
    def teardown_class(cls):
        """teardown_class() after any methods in this class"""
        pass

    def shortDescription(self):
        return None


    def test_view_set1(self):
        """
        No auth example
        """
        api_request = APIRequestFactory().get("")
        detail_view = ConfigViewSet.as_view({'get': 'retrieve'})
        response = detail_view(api_request, pk=self.config.pk)
        self.assertEqual(response.status_code, 401)

    def test_view_set2(self):
        """
        Auth using force_authenticate
        """
        factory = APIRequestFactory()
        user = User.objects.get(username=self.username)
        detail_view = ConfigViewSet.as_view({'get': 'retrieve'})

        # Make an authenticated request to the view...
        api_request = factory.get('')
        force_authenticate(api_request, user=user)
        response = detail_view(api_request, pk=self.config.pk)
        self.assertEqual(response.status_code, 200)

我将它与 django-nose 测试运行器一起使用,它似乎运行良好。希望它可以帮助那些在他们的视图集上启用了身份验证的人。

【讨论】:

    【解决方案2】:

    我找到了一种方法来做到这一点,而无需手动创建正确的视图集并为其提供动作映射:

    from django.core.urlresolvers import reverse, resolve
    ...
    url = reverse('cat-list')
    req = factory.get(url)
    view = resolve(url).func
    response = view(req)
    response.render()
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,并且能够找到解决方案。

      查看源代码,看起来视图期望有一个参数“actions”,它有一个方法 items(所以,一个 dict)。

      https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/viewsets.py#L69

      这是您得到错误的来源。您必须使用包含该视图集允许的操作的 dict 指定参数操作,然后才能正确测试该视图集。

      一般映射如下:

      {
          'get': 'retrieve',
          'put': 'update',
          'patch': 'partial_update',
          'delete': 'destroy'
      }
      

      http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers

      在您的情况下,您需要 {'get': 'retrieve'} 像这样:

      class ViewSetTest(TestCase):
          def test_view_set(self):
              factory = APIRequestFactory()
              view = CatViewSet.as_view(actions={'get': 'retrieve'}) # <-- Changed line
              cat = Cat(name="bob")
              cat.save()
      
              request = factory.get(reverse('cat-detail', args=(cat.pk,)))
              response = view(request)
      

      编辑:您实际上需要指定所需的操作。更改了代码和 cmets 以反映这一点。

      【讨论】:

        【解决方案4】:

        我想我找到了正确的语法,但不确定它是否是传统的(对 Django 来说还是新的):

        def test_view_set(self):
            request = APIRequestFactory().get("")
            cat_detail = CatViewSet.as_view({'get': 'retrieve'})
            cat = Cat.objects.create(name="bob")
            response = cat_detail(request, pk=cat.pk)
            self.assertEqual(response.status_code, 200)
        

        所以现在这通过了,我可以分配 request.user,这允许我自定义 CatViewSet 下的检索方法以考虑用户。

        【讨论】:

          【解决方案5】:

          我想这是你的最后一行。您需要调用 CatViewSet as_view()。我会选择:

          response = view(request)
          

          假设你已经定义了view = CatViewSet.as_view()

          编辑:

          你能显示你的views.py吗?具体来说,您使用了哪种ViewSet?我正在研究 DRF 代码,看起来您可能没有任何操作映射到您的 ViewSet,这会触发错误。

          【讨论】:

          • 最后一行,我可以进入调试器之前的调试器,一切都已定义,但我认为将请求传递到 ViewSet 可能有什么特别之处?
          • 尝试在最后一行传递pk。查看同一页面上的渲染响应示例。第三行:response = view(request, pk='4')
          • hmm 我试过传入,view(request, pk=cat.pk) 和 view(request, cat.pk) 的错误相同
          • 您可以发布您的urls.py 和views.py 文件吗?此外,您可以尝试将倒数第二行更改为使用硬编码路径,以确保它在使用反向之前有效。
          猜你喜欢
          • 2018-10-01
          • 1970-01-01
          • 2017-01-05
          • 1970-01-01
          • 2020-08-26
          • 2014-07-01
          • 2016-06-25
          • 1970-01-01
          • 2020-06-07
          相关资源
          最近更新 更多