django-simple-captcha
django-simple-captcha copied to clipboard
CAPTCHA_TEST_MODE doesn't work with override_settings decorator
Django provides some decorators to modify settings on a per-test or per-test-class basis: django.test.override_settings and django.test.modify_settings (https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings).
However, it seems django-simple-captcha reads and stores the project's value of settings.CAPTCHA_TEST_MODE (and other settings) only the first time captcha.conf.settings is imported. Consequently, one must either specify CAPTCHA_TEST_MODE globally for all tests in a custom settings.py, or monkey-patch captcha.conf.settings...
It would make testing easier if settings were always read on-the-fly, rather than cached.
Or, failing that, it would be good to at least provide notice in the config documentation (particularly for CAPTCHA_TEST_MODE) that they cannot be overridden by override_settings and modify_settings.
agree: neither @django.test.override_settings nor django.conf.settings worked for me. I can see them altered, but since captcha.conf.settings is only imported the first time, these are ignored. Interestingly, I see the tests(django-simple-captcha/captcha/tests/tests.py) overriding these on the fly, but pretty sure those aren't passing(test_test_mode_issue15).
I suggest to use django-appsettings or similar project to manage application settings.
There is a solution for this. To make the CAPTCHA_TEST_MODE work, it needs to be set to True and the form will only be valid if you set captcha_0 and captcha_1. captcha_0 needs a hash (I copied one from my browser) and captcha_1 needs to contain "PASSED" (can be lower or uppercase) in string format.
for example: "captcha_0": "8e10ebf60c5f23fd6e6a9959853730cd69062a15", "captcha_1": "PASSED",
Hello, My solution is to detect if test are running or not. In settings.py file:
# Allow to detect if test are running or not
TESTING = bool(len(sys.argv) > 1 and sys.argv[1] == 'test')
# When set to True, the string “PASSED” (any case) will be accepted as a valid response to any CAPTCHA. Use this for testing purposes.
CAPTCHA_TEST_MODE = TESTING
Now, if TESTING is True, CAPTCHA_TEST_MODE is also set to True and you can validate your form as usually. In tests.py file:
def test_form(self):
"""Test Form with captcha"""
form_data = {
'captcha_0': "dummy_value",
'captcha_1': "PASSED",
}
form = MyForm(data=form_data)
is_valid = form.is_valid()
if not is_valid:
print(form.errors)
self.assertTrue(is_valid)
FTR neither does this work:
try:
settings.CAPTCHA_TEST_MODE = True
response = self.client.post(
self.register_url,
{
"username": username,
"password1": "password",
"password2": "password",
"email": "[email protected]",
"captcha_0": "PASSED",
"captcha_1": "PASSED",
},
follow=follow,
)
finally:
settings.CAPTCHA_TEST_MODE = False
This is the only way how I managed to get it working:
try:
# https://github.com/mbi/django-simple-captcha/issues/84
from captcha.conf import settings as captcha_settings
captcha_settings.CAPTCHA_TEST_MODE = True
response = self.client.post(
self.register_url,
{
"username": username,
"password1": "password",
"password2": "password",
"email": "[email protected]",
"captcha_0": "PASSED",
"captcha_1": "PASSED",
},
follow=follow,
)
finally:
captcha_settings.CAPTCHA_TEST_MODE = False
This worked for me:
from django.test import TestCase
from captcha.conf import settings as captcha_settings
class TestRandomForms(TestCase):
databases = "__all__"
@classmethod
def setUpClass(cls):
super().setUpClass()
captcha_settings.CAPTCHA_TEST_MODE = True
@classmethod
def tearDownClass(cls):
super().tearDownClass()
captcha_settings.CAPTCHA_TEST_MODE = False
def test_random_form_valid(self):
. . .
form_data = {
. . .
'captcha_0': 'PASSED',
'captcha_1': 'PASSED',
.
}
form = RandomForm(data=form_data)
self.assertTrue(form.is_valid())
I hope it helps.