#django #pytest
#django #pytest
Вопрос:
Что у меня есть
В методе сохранения формы вызываются две функции: generate_random_password
и send_email
, мне нужно знать, были ли вызваны эти функции, потому что generate_random_password
присваивает пароль случайным образом каждому новому созданному пользователю и send_email
отправляет пользователю уведомление по электронной почте с учетными данными, сгенерированным паролем и пользователем для входа в систему. Важно знать, были ли эти функции выполнены правильно в режиме сохранения.
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, но я не знаю, как утверждать, были ли вызваны эти функции.
Комментарии:
1. Разве вы не можете просто добавить
print("in function_name")
оператор внутри каждой функции?2. @GAEfan Я говорю о тестировании, мне нужно подтвердить, была ли вызвана одна функция внутри моего метода сохранения формы. проверьте
The problem
раздел.3. Вы хотите издеваться над функциями или вы хотите, чтобы они работали как обычно и просто проверяли, были ли они вызваны?
4. В первом случае вы можете использовать
@mock.patch
, во втором случае вы могли бы использоватьmocker.spy
frompytest-mock
.5. @MrBeanBremen для первой функции, которая генерирует случайный пароль, я хотел бы только знать, была ли вызвана эта функция, чтобы узнать, был ли сгенерирован пароль, для отправки электронных писем, если я хочу сделать макет, чтобы мне не приходилось отправлять сообщение каждый раз, когда я выполняю тест, а также проверять, что это было вызвано.
Ответ №1:
Из того, что вы написали в комментариях, я бы предположил, что издевательство над обеими функциями может иметь смысл, поскольку вам, вероятно, на самом деле не нужен случайный пароль в вашем тесте. Вы могли бы сделать что-то вроде:
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
, но, на мой взгляд, менее удобно.
Комментарии:
1. Когда вы используете
mocked_generate_password
, что вы имеете в виду?2. Это макет для
generate_random_password
— я адаптировал ответ, чтобы сделать это более понятным. В примере он настроен на то, чтобы всегда возвращать «swordfish».3. Во втором примере с
pyest-mock
, который вы используетеmocked_generate_password.assert_called_once()
, мне нужно сохранитьmocker.patch
в переменной likemocked_send_mail = mocker.patch("app_todotransito_co.users.forms.send_mail")
, чтобы иметь возможность утверждать этот метод likemocked_send_mail.assert_called_once()
, потому что, когда я запускаю тест, это выдаетсяNameError: name 'generate_random_password' is not defined
.4. Ах, извините, забыл это адаптировать. Не на ПК, поэтому я допускаю ошибки…
5. Вызывающий код должен отображаться как протестированный, сами функции, замененные на макет (like
send_mail
), нет.