Open romlok opened 9 years ago
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": "new-tester@example.com",
"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": "new-tester@example.com",
"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.
Seeing the 2 previous answers which are actually the only ones working, maybe the package should provide it's own decorator/context manager eg something like '@ignore_captcha_errors'? Starting from there, I wrote this small piece of code and tested it and it works!
from captcha.conf import settings as captcha_settings
from django.test.utils import TestContextDecorator
class ignore_captcha_errors(TestContextDecorator):
def __init__(self):
super().__init__()
self.captcha_test_mode = captcha_settings.CAPTCHA_TEST_MODE
def enable(self):
captcha_settings.CAPTCHA_TEST_MODE = True
def disable(self):
captcha_settings.CAPTCHA_TEST_MODE = self.captcha_test_mode
def decorate_class(self, cls):
from django.test import SimpleTestCase
if not issubclass(cls, SimpleTestCase):
raise ValueError(
"Only subclasses of Django SimpleTestCase can be decorated "
"with ignore_captcha_errors"
)
self.captcha_test_mode = captcha_settings.CAPTCHA_TEST_MODE
return cls
To be used as
class TestSomething(SimpleTestCase):
@ignore_captcha_errors()
def test_something(self):
response = self.client.post(my_url, {... 'captcha_0': 'whatever', 'captcha_1': 'passed'}, follow=True)
or
class TestSomething(SimpleTestCase):
def test_something(self):
with ignore_captcha_errors():
response = self.client.post(my_url, {... 'captcha_0': 'whatever', 'captcha_1': 'passed'}, follow=True)
I'll do a PR for that
Django provides some decorators to modify settings on a per-test or per-test-class basis:
django.test.override_settings
anddjango.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 timecaptcha.conf.settings
is imported. Consequently, one must either specify CAPTCHA_TEST_MODE globally for all tests in a custom settings.py, or monkey-patchcaptcha.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
andmodify_settings
.