How to unit test a form with a captcha field in django?
I know this is an old post, but django-simple-captcha now has a setting CAPTCHA_TEST_MODE which makes the captcha succeed if you supply the value 'PASSED'. You just have to make sure to send something for both of the captcha input fields:
post_data['captcha_0'] = 'dummy-value'
post_data['captcha_1'] = 'PASSED'
self.client.post(url, data=post_data)
The CAPTCHA_TEST_MODE setting should only be used during tests. My settings.py:
if 'test' in sys.argv:
CAPTCHA_TEST_MODE = True
Here's the way I got around it. Import the model that actually holds Captcha info:
from captcha.models import CaptchaStore
First, I check that the test captcha table is empty:
captcha_count = CaptchaStore.objects.count()
self.failUnlessEqual(captcha_count, 0)
After loading the page (in this case, it's a registration page), check that there's a new captcha object instance:
captcha_count = CaptchaStore.objects.count()
self.failUnlessEqual(captcha_count, 1)
Then, I retrieve the captcha instance data and POST that with the form. In my case, the POST expects 'captcha_0' to contain the hashkey, and 'captcha_1' to contain the response.
captcha = CaptchaStore.objects.all()[0]
registration_data = { # other registration data here
'captcha_0': captcha.hashkey,
'captcha_1': captcha.response }
You may need to tweak this a little if you start with CaptchaStore instances before you run this test. Hope that helps.
I unit tested it by mocking the ReCaptchaField. First, I've added the recaptcha field in the constructor. It cannot be added as a regular field because you won't be able to mock it (once the code is evaluated before the mock is being applied):
class MyForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
# Add captcha in the constructor to allow mock it
self.fields["captcha"] = ReCaptchaField()
Then, I just replaced the ReCaptchaField by a not required CharField. This way, I'm trusting django-recaptcha will work. I can test only my own stuff:
@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)
One solution is have a setting "testing" that is either true or false. And then just
if not testing:
# do captcha stuff here
It's simple and easy, and an easy toggle.