【问题标题】:Django testing, mock valid requests to urlsDjango测试,模拟对url的有效请求
【发布时间】:2016-07-26 12:20:58
【问题描述】:

我的 urls.py 文件中有一堆带有 login_required 装饰器的 url

# Index Page
url(r'^$', login_required(views.IndexPage.as_view()), name='index'),

# Schedule urls
url(r'^schedules/$', login_required(views.ScheduleListView.as_view()),
    name='schedule-list'),
url(r'^schedule/(?P<pk>[\d]+)/$',
    login_required(views.ScheduleDetailView.as_view()),
    name='schedule-detail'),
url(r'^schedule-freeze/(?P<pk>[\d]+)/$',
    login_required(views.freezeSchedule),
    name='schedule-freeze'),
url(r'^schedule-create/$', login_required(views.ScheduleCreate.as_view()),
    name='schedule-create'),
url(r'^schedule-delete/(?P<pk>[\d]+)$',
    login_required(views.ScheduleDelete.as_view()),
    name='schedule-delete'),
url(r'^schedule-update/(?P<pk>[\d]+)/$',
    login_required(views.ScheduleUpdate.as_view()),
    name='schedule-update'),
url(r'^schedule-generate/(?P<pk>[\d]+)/$',
    login_required(views.scheduleGenerate), name='schedule-generate'),

# Client urls
url(r'^clients/$', login_required(views.ClientList.as_view()),
    name='client-list'),
url(r'^client/(?P<slug>[\w-]+)/$',
    login_required(views.ClientDetail.as_view()), name='client-detail'),
url(r'^client-create/$', login_required(views.ClientCreate.as_view()),
    name='client-create'),
url(r'^client-delete/(?P<slug>[\w-]+)/$',
    login_required(views.ClientDelete.as_view()), name='client-delete'),
url(r'^client-update/(?P<slug>[\w-]+)/$',
    login_required(views.ClientUpdate.as_view()), name='client-update'),

# And so on ....

对于每个受限视图,我都在尝试编写一个测试,以确保在尝试访问视图时将未经授权的用户重定向到登录页面。如果可能的话,我希望能够在一个代码块中实现这一点,而不是为每个 URL 编写一个测试。

我尝试过类似以下的方法:

list_urls = [e for e in get_resolver(urls).reverse_dict.keys() if isinstance(e, str)]

for url in list_urls:
    # Fetches the urlpath e.g. 'client-list'
    namedspaced_url = 'reports:' + url
    path = reverse(namedspaced_url)
    response = self.client.get(path)
    self.assertEqual(response.status_code, 302)
    self.assertRedirects(response, reverse('login') + '?next=' + path)

list_urls 返回我的 urls.py 文件中所有命名 url 的列表,即['schedule-create', 'server-detail', 'schedule-list', 'schedule-update', 'index', ....]

问题

这段代码:reverse(namedspaced_url)

这导致问题的原因是每个 url 都有不同的正则表达式模式,即一些采取 slugs 一些采取 pk 的

所以path = reverse(namedspaced_url) 行将适用于简单的 URL,例如指向 ListViews 的那些,但对于更复杂的 URL 会失败,例如指向需要 slug/pk 的 DetailViews 的那些,即path = reverse(namedspaces_url, args=[1945])

是否可以暂时覆盖/忽略 Django 的模式匹配/路由来强制请求通过(不管传递的 args)

或者我是否必须使用有效的 kwargs/args 为每个 URL 手动编写测试以满足正则表达式?

我可以采取另一种完全不同的方法来为我的所有 login_required() 视图编写测试吗?

更新 使用内省我想出了以下怪物来解决我的问题

def test_page_redirects_for_unauthorised_users(self):
    url_dict = get_resolver(urls).reverse_dict
    url_list = [e for e in get_resolver(urls).reverse_dict.keys() if
                isinstance(e, str)]
    for url in url_list:
        patterns = url_dict[url][0][0][1]
        matches = [1 if e == 'pk' else "slug" if e == 'slug' else None for
                   e in patterns]
        path = reverse('reports:' + url, args=matches)
        response = self.client.get(path)
        self.assertEqual(response.status_code, 302)
        self.assertRedirects(response, reverse('login') + '?next=' + path)

【问题讨论】:

  • 您应该能够自省 url 解析器中的项目以获取命名参数。然后,您可以使用合适的值调用 reverse,例如1945pk,或my-slugslug
  • 你又来了!我玩弄了这个想法并被建议不要这样做,因为这意味着我必须想出示例值来匹配我的网址中的每个正则表达式模式(这很快就会变得混乱)。我想知道是否有更清洁的解决方案,因为我确信以前有人必须解决这个问题。不过谢谢你的建议! (如果这是解决此问题的唯一已知方法,我想我只需要这样做)

标签: python django unit-testing


【解决方案1】:

您正在尝试测试一些非常复杂的东西,因为您决定使用login_requireddecorate the urlconf

为什么不改为decorate the class?这样,您可以简单地测试每个类以确保它具有 login_required 装饰器。这消除了模拟 slug 和 pk 正则表达式值的需要。

【讨论】:

  • 我不是很关注,所以你是说通过将逻辑移动到不同的地方我不再需要担心测试? (基本假设是我可以相信 Django 的 login_required 装饰器会做正确的事情,即重定向用户)。这与在 urls.py 中使用 login_required 包装 url 有何不同?
  • @JackEvans 您可以装饰 url 或视图 - 最终结果是相同的,因为未经身份验证的用户将被重定向。在您的情况下,装饰视图更容易测试,因为您可以测试视图是否具有装饰器,而不必像您尝试的那样在测试中模拟 url pkslug 值。
【解决方案2】:

在 proect_name/project_name/urls.py urlpatterns = [ url(r'', login_required(include('app_name.urls')), ]

这会将 login_required 应用于 project_name/app_name/urls.py 中的所有 url

【讨论】:

  • 这有助于整个应用程序受到限制的情况。但我受到的限制之一是,将来可能会添加不需要登录的新路线。在测试每个单独的视图重定向到登录视图时,这也有同样的问题
  • @login_required(login_url="login_url/")放在你想在views.py中限制的每个视图之前
猜你喜欢
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
  • 2015-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多