Форма действительна в модульном тесте, даже если ValidationError был поднят


Я проверяю свое представление на обработку недопустимых данных формы. В моем тестовом случае я отправляю форму с отсутствующим полем и ожидаю, что view обработает его, отобразив сообщение об ошибке. Вот соответствующий фрагмент из clean в моей форме:

Форма:

def clean(self):
    first_name = self.cleaned_data.get('first_name', '')
    last_name = self.cleaned_data.get('last_name', '')

    if not first_name or not last_name:
        print('Form is invalid')
        # This is always executed.
        raise ValidationError('All fields are required.')

    # other stuff

ValidationError всегда поднимается, однако, form.is_valid() возвращает True Тем не менее:

Вид:

form = forms.EditProfileForm(request.POST, instance=user)

if form.is_valid():
    print('Form is valid')
    # Update user data. 
    # Always executed even though error was raised.

Это приводит к провалу теста, так как я утверждаю, что проверка формы не будет выполнена:

Тестовый Случай:

def test_invalid_data(self):
    formdata = {
        'first_name': 'Bruno',
        'last_name': '', # Missing data.
        'email': 'bruno@schulz.pl',
        'password': 'metamorphosis'
    }

    request = self.factory.post('/accounts/profile', formdata)
    request.user = self.user
    setup_messages(request)
    response = ProfileView.as_view()(request)

    self.assertEqual(response.status_code, 200)
    self.assertNotEqual(self.user.first_name, formdata['first_name']) # Fails.
    self.assertNotEqual(self.user.last_name, formdata['last_name'])
    self.assertNotEqual(self.user.email, formdata['email'])
    self.assertNotEqual(self.user.username, formdata['email'])

Форма проверка корректно работает на реальном сервере (тестируется "вручную").

Похоже, что ValidationError игнорируется во время оценки TestCase.

* тестовый вывод:

.Form is invalid
F..Form is valid
.
======================================================================
FAIL: test_invalid_data (.tests.ProfileTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/apps/accounts/tests.py", line 114, in test_invalid_data
    self.assertNotEqual(self.user.first_name, formdata['first_name'])
AssertionError: 'Bruno' == 'Bruno'
2 2

2 ответа:

Вы не показали свой полный вид, но обычно вы это делаете return redirect('/success-url/') после успешного сообщения. Если ошибка проверки была проигнорирована, то представление будет перенаправлено с кодом состояния 302, и тест завершится неудачей в предыдущей строке self.assertEqual(response.status_code, 200)

Вы можете получить доступ к форме из представления с помощью form = response.context['form']. Если вы проверите form.is_valid() или form.errors в своем тесте, вы увидите, что ваш ValidationError не был проигнорирован.

Ваша проблема заключается в том, что ваши проверки не проверяют то, что вы думаете. При проверке форма модели, экземпляр модифицирован. Если вы хотите проверить, был ли пользователь изменен в базе данных, вам нужно сначала обновить его из базы данных.
self.user.refresh_from_db()

Попробуйте использовать self.add_error('last_name', your_validation_error) вместо того, чтобы просто вызывать ошибку:

def clean(self):
    first_name = self.cleaned_data.get('first_name', '')
    last_name = self.cleaned_data.get('last_name', '')

    if not first_name:
        print('Form is invalid: missing first name')
        # define but don't raise the error
        missing_first_name = ValidationError('First name field is required.')
        self.add_error('first_name', missing_first_name)
    if not last_name:
        print('Form is invalid: missing last name')
        # define but don't raise error
        missing_last_name = ValidationError('Last name field is required.')
        self.add_error('last_name', missing_last_name)
        # you could also add a string instead of an Error, but a ValidationError is recommended:
        # self.add_error('last_name', 'the last name field is missing')

# other stuff

Теперь form.is_valid() вернет False.

Этот так называемый ответ привел меня к решению, и вот документы add_error. Вы должны добавить ошибку в форму с помощью self.add_error, а не вызывать ошибку проверки.

(я нашел этот пример в docs , где ошибка возникает вместо того, чтобы передаваться в self.add_error, что может быть источником вашей путаницы, но это было для очистки конкретного поле. Это был не метод form.clean(). Я предполагаю, что вы могли бы поймать эту ошибку, а затем добавить ее с self.add_error... но это уже другая проблема, которую нужно решить. Дело в том, что я не видел примеров в документах, где form.clean() должен вызывать ValidationError. Вместо этого он должен добавить его в форму.)