Created
August 3, 2020 21:15
-
-
Save smithdc1/689894b2d68ec333605d0bb021d614ac to your computer and use it in GitHub Desktop.
#24782 -- Added TestCase.assertFormValid
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.utils.translation import ngettext_lazy | |
from django.core.validators import validate_email | |
from django.forms import Form, CharField, IntegerField, PasswordInput | |
from django.conf import settings | |
from django.forms.utils import ErrorList, ErrorDict, ValidationError | |
import django | |
settings.configure() | |
django.setup() | |
class TestForm(Form): | |
name = CharField(label='Name', max_length=4) | |
age = IntegerField(label='Age') | |
class TestFormEmail(Form): | |
email = CharField(label='email', max_length=4, validators=[validate_email]) | |
class UserRegistration(Form): | |
username = CharField(max_length=10) | |
password1 = CharField(widget=PasswordInput) | |
password2 = CharField(widget=PasswordInput) | |
def clean(self): | |
if (self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and | |
self.cleaned_data['password1'] != self.cleaned_data['password2']): | |
raise ValidationError('Please make sure your passwords match.') | |
return self.cleaned_data | |
email_form = TestFormEmail(data={ | |
'email': 'not_an_email_address' | |
}) | |
email_form.is_valid() | |
test_form = TestForm(data={ | |
'name': 'John', | |
'age': None, | |
}) | |
test_form.is_valid() | |
password_form = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) | |
password_form.is_valid() | |
# manually build an error dict for test_form | |
required_error = ValidationError(message='This field is required.', code='required') | |
error_dict = ErrorDict( | |
{'age': ErrorList(required_error)} | |
) | |
assert error_dict == test_form.errors | |
# manually build error dict for email_form | |
email_error = ValidationError(message='Enter a valid email address.', code='invalid', | |
params={'value': 'not_an_email_address'}) | |
max_length_error = ValidationError( | |
message=ngettext_lazy( | |
'Ensure this value has at most %(limit_value)d character (it has %(show_value)d).', | |
'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).', | |
'limit_value'), | |
code='max_length', | |
params={'limit_value': 4, 'show_value': 20, 'value': 'not_an_email_address'} | |
) | |
# ValidationErrors are equal to those generated by the form | |
assert email_form.errors['email'].data[0] == email_error | |
assert email_form.errors['email'].data[1] == max_length_error | |
# Manually built ErrorDict is equal to form.errors | |
email_error_dict = ErrorDict( | |
{'email': ErrorList([email_error, max_length_error])} | |
) | |
print('Can manually build ErrorDict') | |
print(email_form.errors == email_error_dict) | |
# But -- code and params are not important | |
print('\nCode and Params are not important') | |
print(email_form.errors == ErrorDict( | |
{'email': ErrorList( | |
[ | |
ValidationError('Enter a valid email address.'), | |
ValidationError('Ensure this value has at most 4 characters (it has 20).') | |
])} | |
)) | |
# and order is | |
print('\nErrorDict ordering is important') | |
print(email_form.errors == ErrorDict( | |
{'email': ErrorList( | |
[ | |
ValidationError('Ensure this value has at most 4 characters (it has 20).'), | |
ValidationError('Enter a valid email address.') | |
])} | |
)) | |
# NON_FIELD_ERRORS | |
# ValidationError was created without a code -- code will therefore be 'None'. | |
# Therefore testing against 'code' doesn't really work for non-field errors, unless users pass in a 'code' | |
# It _could_ be based upon ValidationError equality or message, but need to type it out in full. | |
print('\nCustom errors may not have a "code"') | |
print(password_form.errors['__all__'].data[0].code) | |
# Conclusions | |
# 1. Equality at the ErrorDict level is not a robust enough way to | |
# test for errors raised on a form. But could check for each ValidationError | |
# in turn | |
# 2. Building validation errors to pass the validation equality test is too | |
# hard. Max length error above is good, it's just too difficult to | |
# expect a user to be able to pass in message/params/code correctly. | |
# 3. Non field errors (e.g. password matching) is likely to not have a code | |
# passed to it, so testing on 'code' alone is not enough. Example here is from | |
# Django's own test suite. | |
# Options | |
# 1. My conclusion is wrong, it's ok to expect a user to pass in all of the | |
# requirements to build a ValidationError | |
# 2. Carry on with only testing for 'code'. This is simpler as in most cases | |
# the codes are fairly short. Downside it is not as robust as the equality error | |
# unlikely to be helpful for non field errors. Potential to pass 'code' check | |
# but still to be a different error. | |
# 3. Simplify function so new functions only test for valid/invalid, and not | |
# the reasons for a form being invalid. | |
# 4. Close ticket as won't fix. Is it worth adding an extra function when | |
# something like the only benefit would be you could write | |
# self.assertequal(form.is_valid(), True) | |
# as | |
# self.assertFormValid(form) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment