【问题标题】:How to unit test a form with a captcha field in django?如何在 django 中对带有验证码字段的表单进行单元测试?
【发布时间】:2011-03-10 17:05:56
【问题描述】:

我想通过汇总表单来对 django 视图进行单元测试。问题是这个表单有一个验证码字段(基于 django-simple-captcha)。

from django import forms
from captcha.fields import CaptchaField

class ContactForm(forms.forms.Form):
    """
    The information needed for being able to download
    """
    lastname = forms.CharField(max_length=30, label='Last name')
    firstname = forms.CharField(max_length=30, label='First name')
    ...
    captcha = CaptchaField()

测试代码:

class ContactFormTest(TestCase):

    def test_submitform(self):
        """Test that the contact page"""
        url = reverse('contact_form')

        form_data = {}
        form_data['firstname'] = 'Paul'
        form_data['lastname'] = 'Macca'
        form_data['captcha'] = '28if'

        response = self.client.post(url, form_data, follow=True)

有没有什么方法可以对这段代码进行单元测试并在测试时去掉验证码?

提前致谢

【问题讨论】:

  • 万一其他人像我一样来到这里,我偶然发现了这篇文章,试图为django-recaptcha 包找到类似的答案;原来他们也有一个设置。他们的文档描述了它的用途:github.com/praekelt/django-recaptcha
  • 对于那些使用 django-recaptcha 并且需要在你的单元测试中发帖的人,你还需要像这样发送“g-recaptcha-response”: self.client.post(url, {"g -recaptcha-response": "PASSED"})

标签: python django unit-testing captcha


【解决方案1】:

我知道这是一篇旧帖子,但 django-simple-captcha 现在有一个设置 CAPTCHA_TEST_MODE,如果您提供值“PASSED”,验证码就会成功。您只需确保为两个验证码输入字段发送一些内容:

post_data['captcha_0'] = 'dummy-value'
post_data['captcha_1'] = 'PASSED'
self.client.post(url, data=post_data)

CAPTCHA_TEST_MODE 设置只能在测试期间使用。我的设置.py:

if 'test' in sys.argv:
    CAPTCHA_TEST_MODE = True 

【讨论】:

  • 现在也可以使用@override_settings(CAPTCHA_TEST_MODE=True) from from django.test import override_settings;但不幸的是,截至 2019 年 2 月,存在一个问题,即该设置仅读取一次 - 当应用程序启动时。见github.com/mbi/django-simple-captcha/issues/84
【解决方案2】:

这是我解决它的方法。导入实际保存验证码信息的模型:

from captcha.models import CaptchaStore

首先,我检查测试验证码表是否为空:

captcha_count = CaptchaStore.objects.count()
self.failUnlessEqual(captcha_count, 0)

加载页面后(本例为注册页面),检查是否有新的验证码对象实例:

captcha_count = CaptchaStore.objects.count()
self.failUnlessEqual(captcha_count, 1)

然后,我检索验证码实例数据并将其与表单一起发布。在我的例子中,POST 期望 'captcha_0' 包含哈希键,而 'captcha_1' 包含响应。

captcha = CaptchaStore.objects.all()[0]
registration_data = { # other registration data here
                     'captcha_0': captcha.hashkey,
                     'captcha_1': captcha.response }

如果您在运行此测试之前从 CaptchaStore 实例开始,您可能需要稍微调整一下。希望对您有所帮助。

【讨论】:

  • 我所做的(在注意到您的回答之前)是解析未绑定的表单 HTML dom = PyQuery('<html><body>{}</body></html>'.format(f.as_p()),从那里获取哈希 hashkey = dom('input[name="captcha_0"]').attr('value'),然后使用它查询数据库。其余的基本相同。希望它希望有人。
【解决方案3】:

我通过模拟 ReCaptchaField 对其进行了单元测试。首先,我在构造函数中添加了 recaptcha 字段。它不能作为常规字段添加,因为您将无法模拟它(一旦在应用模拟之前评估代码):

class MyForm(forms.ModelForm):

    ...

    def __init__(self, *args, **kwargs):
        # Add captcha in the constructor to allow mock it
        self.fields["captcha"] = ReCaptchaField()

然后,我将 ReCaptchaField 替换为不需要的 CharField。这样,我相信 django-recaptcha 会起作用。我只能测试我自己的东西:

@mock.patch("trials.forms.ReCaptchaField", lambda: CharField(required=False))
def test_my_stuff(self):
    response = self.client.post(self.url, data_without_captcha)
    self.assert_my_response_fit_the_needs(response)

【讨论】:

  • 为了避免像我这样健忘的人不需要查找它们,您需要:from django.db.models import CharFieldfrom unittest import mock
【解决方案4】:

一种解决方案是将“测试”设置为真或假。然后就

if not testing:
   # do captcha stuff here

简单易行,切换也很简单。

【讨论】:

  • 它可以工作,但是在将表单导入测试模块之前必须设置 settings.UNIT_TEST = True 。这就是我犯错的原因
  • 你也可以在设置文件中设置测试:if "test" in sys.argv: TESTING = True
【解决方案5】:

另一种解决方案类似于 Jim McGaw 的回答,但不需要空表 CaptchaStore 表。

captcha = CaptchaStore.objects.get(hashkey=CaptchaStore.generate_key())

registration_data = { # other registration data here
                 'captcha_0': captcha.hashkey,
                 'captcha_1': captcha.response }

这将为该测试生成新的验证码。

【讨论】:

    【解决方案6】:

    采用与 Jim McGaw 类似的方法,但使用的是 BeautifulSoup:

    from captcha.models import CaptchaStore
    from BeautifulSoup import BeautifulSoup
    
    data = {...} #The data to post
    soup = BeautifulSoup(self.client.get(url).content)
    for field_name in ('captcha_0', ...): #get fields from the form
        data[field_name] = soup.find('input',{'name':field_name})['value']
    captcha = CaptchaStore.objects.get(hashkey=data['captcha_0'])
    data['captcha_1'] = captcha.challenge
    response = self.client.post(url, data=data)
    
    # check the results
    ...
    

    【讨论】:

      【解决方案7】:

      这是我们的做法。

      @patch("captcha.fields.ReCaptchaField.validate")
      def test_contact_view(self, validate_method):
      
          response = self.client.get(reverse("contact"))
          self.assertEqual(response.status_code, 200)
      
          data = {
              "name": "Bob Johnson",
              "email": "big_johnson@home.com",
              "phone": "800-212-2001",
              "subject": "I want Axis!",
              "message": "This is a giant\nThree liner..\nLove ya\n",
              "captcha": "XXX",
          }
          validate_method.return_value = True
          response = self.client.post(reverse("contact"), data=data)
      
          self.assertEqual(response.status_code, 302)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-24
        • 2013-05-08
        • 2015-10-01
        • 1970-01-01
        • 2015-04-11
        • 2022-01-14
        • 2013-09-29
        相关资源
        最近更新 更多