首页 > 解决方案 > 如果在 Django 表单保存方法中调用了一个函数,如何使用 pytest 检查?

问题描述

是)我有的

在表单的 save 方法中调用了两个函数:generate_random_passwordsend_email,我需要知道是否调用了这些函数,因为generate_random_password它会为每个新创建的用户随机分配一个密码,并向用户send_email发送一封电子邮件通知,其中包含凭据、生成的密码和用户登录。重要的是要知道这些函数是否在 save 中正确执行。

class UserAdminCreationForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ()
 
    def save(self, commit: bool = True) -> U:
        user = super(UserAdminCreationForm, self).save(commit=False)
        data = self.cleaned_data
        # Set random password for every new user
        random_password = generate_random_password()
        user.set_password(random_password)
        # Send email confirmation with credentials to login
        email = data.get("email")
        html_message = render_to_string(
            template_name="mails/user_creation_notification.html",
            context={"email": email, "password": random_password},
        )
        # Strip the html tag. So people can see the pure text at least.
        plain_message = strip_tags(html_message)
        send_mail(
            subject="Bienvenido a TodoTránsito",
            message=plain_message,
            recipient_list=[email],
            html_message=html_message,
        )
        # Save into the DB the new user
        if commit:
            user.save()
        return user

问题

pytest用来测试我的 Django 代码,但我不知道如何断言这些函数是否被调用。

标签: djangopytest

解决方案


根据您在评论中写的内容,我猜想模拟这两个函数可能是有意义的,因为您可能并不真的需要在测试中使用随机密码。您可以执行以下操作:

from unittest import mock

@mock.patch('some_module.generate_random_password', return_value='swordfish')
@mock.patch('some_module.send_mail')
def test_save_user(mocked_send_mail, mocked_generate_password):
    user_form = create_user_form()  # whatever you do to create the form in the test
    user_from.save()
    mocked_generate_password.assert_called_once()
    mocked_send_mail.assert_called_once()
    # or mocked_send_mail.assert_called_once_with(...) if you want to check the parameters it was called with

请注意,您必须确保模拟正确的模块,例如在测试代码中使用的模块(请参阅修补位置)。
在这种情况下generate_random_password,被替换为总是返回相同密码send_mail的模拟,并被替换为除了记录调用之外什么都不做的模拟。两个模拟都可以通过测试中的参数访问,这些参数由patch装饰器注入(最后一个装饰器在前,参数名称是任意的)。

如果您安装pytest-mock,您将获得mocker夹具,它为您提供相同的功能以及更多功能。相同的代码会是这样的:

def test_save_user(mocker):
    mocked_generate_password = mocker.patch('some_module.generate_random_password', return_value='swordfish')
    mocked_send_mail = mocker.patch('some_module.send_mail')
    user_form = create_user_form()  # whatever you do to create the form in the test
    user_from.save()
    mocked_generate_password.assert_called_once()
    mocked_send_mail.assert_called_once()

如果您现在想使用 real generate_random_password,但仍想查看它是否被调用,则可以mocker.spy改用:

def test_save_user(mocker):
    mocked_generate_password = mocker.spy(some_module, 'generate_random_password')
    mocked_send_mail = mocker.patch('some_module.send_mail')
    user_form = create_user_form()  # whatever you do to create the form in the test
    user_from.save()
    mocked_generate_password.assert_called_once()
    mocked_send_mail.assert_called_once()

请注意unittest.mock.patch.object,在我看来,您可以使用 实现相同的功能,但不太方便。


推荐阅读