pythondjangodjango-formspython-unittestdjango-unittest

Test validate_unique raises ValidationError Django forms


I have a ModelForm called SignUpForm located in myproj.accounts.forms

SignUpForm overrides Django's validate_unique so that the 'email' field is excluded from 'unique' validation as required by the model's unique=True (this is dealt with later in the view). Everything works as expected.

I now want to test the code by raising a ValidationError when self.instance.validate_unique(exclude=exclude) is called.

The problem I have is how to use mock to patch the instance.validate_unique so that a ValidationError is raised.

SignUpForm's validate_unique(self) - myproj.accounts.forms

    def validate_unique(self):
        exclude = self._get_validation_exclusions()
        exclude.add('email')

        try:
            self.instance.validate_unique(exclude=exclude)
        except forms.ValidationError as e:
            self._update_errors(e)

This test works, but it does not raise the error on the method (validate_unique) and not the instance (self.instance.validate_unique).

    def test_validate_unique_raises_exception(self):
        with patch.object(SignUpForm, 'validate_unique') as mock_method:
            mock_method.side_effect = Exception(ValidationError)
            data = {"email": 'someone@somewhere.com',
                    'full_name': A User,
                    "password": "A19A23CD",
                    }
            form = SignUpForm(data)
            self.assertRaises(ValidationError)

My question is how can I raise a ValidationError using mock when self.instance.validate_unique is called?


Solution

  • The solution in the end was to rewrite my override of validate_unique() so that a ValidationError always occurs when an existing email address is detected, after which 'email' can be removed from error_dict using .pop('email'):

        def validate_unique(self):
            exclude = self._get_validation_exclusions()
            try:
                self.instance.validate_unique(exclude=exclude)
    
            except forms.ValidationError as e:
                e.error_dict.pop('email')
                self._update_errors(e)
    

    Which can be tested like so:

    class SignUpFormTest(TestCase):
    
        def setUp(self):
            self.email = "testclient@example.com"
            self.full_name = "test user"
    
            self.user = CustomUser.objects.create_user(email='a@a.com',
                                                       full_name='A Aaa',
                                                       password='A19A23CD',
                                                       )
    
        @patch.object(SignUpForm, '_get_validation_exclusions')
        def test_unique(self, mock_it):
            data = {
                "email": self.user.email,
                'full_name': self.user.full_name,
                "password": "A19A23CD",
            }
    
            mock_it.return_value = {'company', 'id', 'date_joined', 'last_login', 'is_active', 'is_verified', 'is_superuser', 'is_staff'}
    
            form = SignUpForm(data)
            self.assertFalse(form.has_error('email'))
            self.assertRaises(ValidationError, form.instance.validate_unique)