Testing using mock

I am new to testing and I am having trouble with a test using mock, which is fails as follows:

FAIL: test_success (account.tests.test_forms_RegistrationForm.RegistrationFormTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1325, in patched
    return func(*newargs, **newkeywargs)
  File "/Users/user/Documents/dev/prog/account/tests/test_forms_RegistrationForm.py", line 132, in test_success
    self.assertEqual(password_changed.call_count, 1)
AssertionError: 0 != 1

----------------------------------------------------------------------
Ran 28 tests in 2.338s

Here is the test:

@mock.patch("django.contrib.auth.password_validation.password_changed")
def test_success(self, password_changed):
    # The success case.
    data = {
        "email": "jsmith@example.com",
        "full_name": "John Smith",
        "password": "Ashjylioi1",
    }
    form = RegistrationForm(data)
    self.assertTrue(form.is_valid())
    form.save(commit=False)
    self.assertEqual(password_changed.call_count, 0)
    u = form.save()
    self.assertEqual(password_changed.call_count, 1)

    self.assertEqual(
        repr(u), "<{}: jsmith@example.com>".format(get_user_model().__name__)
    )

Here is the form:

class RegistrationForm(forms.ModelForm):

  error_messages = {
      "duplicate_email": _("A user with that email already exists."),
      "password_mismatch": _("The two password fields didn't match."),
  }

  email = forms.EmailField(max_length=254,
                           error_messages={'invalid': 'Please enter a valid email address.'},
                           label=_('Email address'),
                           widget=forms.EmailInput(attrs={'class': 'form-control mb-4',
                                                          }))

  full_name = forms.CharField(label=_('Full name'),
                              widget=forms.TextInput(attrs={'class': 'form-control mb-4',
                                                            }))
  password = forms.CharField(label=_('Password'),
                             widget=forms.PasswordInput(attrs={'class': 'form-control mb-4',
                                                               'autocomplete': 'new-password',
                                                               'id': 'psw',
                                                               }))

  class Meta:
      model = get_user_model()
      fields = ('full_name', 'email', 'password')

  def clean_password(self):
      password = self.cleaned_data.get("password")
      return password

  def clean_email(self):
      email = self.cleaned_data['email']
      if User.objects.filter(email=email).exists():
          raise forms.ValidationError(
              mark_safe(
                  _(f'A user with that email already exists, click this <br><a href="{reverse("account:pwdreset")}">'
                    'Password Reset</a> link to recover your account.'))
          )

      return email

  def _post_clean(self):
      super()._post_clean()
      # Validate the password after self.instance is updated with form data
      # by super().
      password = self.cleaned_data.get("password")
      if password:
          try:
              password_validation.validate_password(password, self.instance)
          except ValidationError as error:
              self.add_error("password", error)

  def save(self, commit=True):
      """
      Save user.
      Save the provided password in hashed format.
      :return custom_user.models.EmailUser: user
      """
      user = super().save(commit=False)
      user.set_password(self.cleaned_data["password"])

      return user

Hi pcd1000,

You need to patch the function in the module that it’s being used in. In this case, it should be something like patch('account.forms.password_validation'). You don’t patch the logic at where’s it’s defined.

Hi Tim, many thanks for your reply.

In the end I corrected def save() in the form to:

def save(self, commit=True):
      """
      Save user.
      Save the provided password in hashed format.
      :return custom_user.models.EmailUser: user
      """
      user = super().save(commit=False)
      user.set_password(self.cleaned_data["password"])

     if commit:
            user.save()
     return user