【问题标题】:Django forms which needs the request , makes testing harder?需要请求的 Django 表单,使测试更难?
【发布时间】:2016-06-05 15:14:28
【问题描述】:

我有许多 django 表单,我将请求作为 kwarg 传递。
我刚刚开始深入测试,似乎需要请求作为参数的测试表单使测试变得更加困难。因为我必须以某种方式创建一个请求,没有它我无法测试我的表单。

那么最好完全避免将请求传递给表单吗?还是另一种解决方法? 我首先这样做的原因是有时我需要 request.user 或 request.session 并根据表单中的信息进行一些清理/设置。

更新:
这是一个示例表单:

class OrderForm(forms.ModelForm):
   def __init__(self, *args, **kwargs):
      self.request = kwargs.pop('request')
      self.user = self.request.user

   def clean(self):
       # Here I have some cross session-field validation
       if self.request.session['has_response'] and self.cleaned_data('status') == 'NEW':
            raise ValidationError()

   def save(self, commit=False):
      self.instance.user = self.user
      return super(OrderForm, self).save(commit=True)

   class Meta:
      model = Order
      fields = ('address', 'city', 'status', ) # more fields

查看代码很简单:

form = OrderForm(request.POST, request=request)

Order 模型还有一个带有一些验证逻辑的 clean() 方法。 会话最多在用户登录期间填充。 我需要那里的会话/用户。

但最重要的问题是 - 将请求和会话传递给表单,考虑测试此表单的选项,这是一个糟糕的设计吗?当表单负责保存对象(包括 request.user)时,我发现它更合乎逻辑。但也许我应该尝试在表单和视图之间拆分它?

【问题讨论】:

  • 你能举个例子吗?默认情况下,render() 将使用 RequestContext 作为 context_instance。
  • 更新了一个与我的大多数表单行为非常匹配的表单。
  • 查看我最近更新的设置会话。

标签: django forms testing request keyword-argument


【解决方案1】:

如果您在clean() 方法中需要它,则可以将请求传递给表单。您可以在这样的测试中使用请求/会话/用户:

from django.test import TestCase, Client
from django.test.client import RequestFactory
from django.contrib.auth.models import AnonymousUser, User
from .views import my_view
from .forms import MyForm
from django.contrib.sessions.middleware import SessionMiddleware

# If Python >= 3.4
from unittest.mock import patch, MagicMock
# Else
from mock import patch, MagicMock

class SimpleTest(TestCase):
    def setUp(self):
        # Create a RequestFactory accessible by the entire class.
        self.factory = RequestFactory()
        # Create a new user object accessible by the entire class.
        self.user = User.objects.create_user(username='username', 
                                 email='email', password='password')

    def test_my_view(self):
        # Create an instance of a GET request.
        request = self.factory.get('/my-url/')

        # Middleware is not supported so simulate a
        # logged-in user by setting request.user.
        request.user = self.user

        # Or add anonymous user to request.
        request.user = AnonymousUser()

        # Test view() at '/my-url/'
        response = my_view(request)
        self.assertEqual(response.status_code, 200)

    @patch('app.models.ModelName.save', MagicMock(name="save"))
    def test_my_form_view_with_factory(self):
        # Set up form data.
        form_data = {'something': 'something'}

        # Create an instance of a POST request.
        request = self.factory.post('/my-form-url/', form_data)

        # Simulate logged-in user
        request.user = self.user

        # Setup session.
        middleware = SessionMiddleware()
        middleware.process_request(request)
        request.session.save()

        # Or you should just be able to do
        request.session['somekey'] = 'test'
        request.session.save()           

        # Get response from form view, and test passing 
        # request/data to form.
        form = MyForm(request=request, data=form_data)
        response = my_form_view(request)

        self.assertTrue(form.is_valid())
        self.assertEqual(response.status_code, 200)

        # If model form you can do 
        self.assertTrue(ModelName.save.called)

    @patch('app.models.ModelName.save', MagicMock(name="save"))
    def test_my_form_view_with_client(self):
        # Use Client instead of RequestFactory.
        self.client = Client()

        # Login with Client.
        self.client.login(username='username', password='password')

        # Set up form data.
        form_data = {'something': 'something'}

        # Get/set session.
        session = self.client.session
        session['somekey'] = 'test'
        session.save()

        # Get response with Client.
        response = self.client.post('/my-form-url/', form_data)
        self.assertEqual(response.status_code, 200)

        # If model form you can do 
        self.assertTrue(ModelName.save.called)

应该大致了解您可以做什么,而不是专门测试。

【讨论】:

  • 对我来说最有用的是这里的RequestFactory。但是这个测试是否允许使用 request.session - 或者它也需要被刺激?考虑到我需要登录才能测试我的一些表单,因为登录涉及一些会话数据的设置
  • RequestFactory 不能登录,但可以模拟并设置会话数据。使用客户端,您可以同时做到这两点。如果您的自定义登录视图以某种方式设置会话数据,您可以使用客户端使用您的自定义视图登录,它将设置会话数据。或者您可以使用请求工厂对您的自定义登录视图执行相同的操作。然后,您可以根据需要编辑会话数据。我会亲自尝试使用 RequestFactory。
  • 谢谢。由于我的表单要求用户登录,我猜刺激登录(将填充会话)的正确位置将在 setUp() 中?因为在用户访问表单之前这是必需的
  • 是的,那么测试类中的所有方法都可以使用它。请记住,如果您需要在测试之间重置数据库,您应该创建单独的类。您也可以在登录后使用assertEqual 确保会话设置正确。
  • 独立班似乎适合我,我想我明白了,我现在可以写一些测试了。我确实认为kwargs 中对表单的请求使测试更加复杂(因为您混合了一些请求处理逻辑),但仍然可能。谢谢,很有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-25
  • 1970-01-01
相关资源
最近更新 更多