После нескольких сценариев один и тот же Mock переиспользуют в тесте: что именно очищает reset_mock, а что сохраняет?
reset_mock() очищает историю вызовов мока и его дочерних моков, но по умолчанию сохраняет настроенные return_value, side_effect, спецификацию и произвольные атрибуты. Чтобы дополнительно сбросить возвращаемое значение или побочный эффект, их нужно явно запросить параметрами return_value=True и side_effect=True.
Моки используются не только для подмены зависимостей, но и для проверки взаимодействий: количества вызовов, аргументов и порядка обращений. Поэтому объект мока накапливает состояние вызовов, а повторное использование одного объекта без очистки приводит к тому, что следующий сценарий видит историю предыдущего.
reset_mock появился как способ переиспользовать настроенную подмену без её полного пересоздания. Однако это именно сброс наблюдаемого состояния, а не возврат объекта к исходному состоянию без каких-либо настроек.
Если несколько сценариев используют один Mock, проверки вроде количества вызовов могут учитывать обращения, выполненные раньше. В результате тест либо ложно падает, либо становится зависимым от порядка выполнения сценариев.
Простое предположение, что reset_mock() удаляет все настройки, тоже опасно. После сброса мок может продолжить возвращать ранее заданное значение или выполнять прежний side_effect, хотя его история вызовов уже пуста.
Без аргументов reset_mock() сбрасывает called, call_count, call_args, call_args_list, method_calls и mock_calls. Состояние дочерних моков, созданных при обращении к атрибутам или методам, также сбрасывается рекурсивно.
По умолчанию настройки поведения сохраняются. К ним относятся return_value, side_effect, spec, spec_set, wraps и созданные атрибуты. Поэтому после сброса мок остаётся той же подменой, но начинает с чистой историей взаимодействий.
Параметр return_value=True дополнительно сбрасывает возвращаемое значение, а side_effect=True удаляет побочный эффект. Эти параметры следует применять осознанно: если мок специально настроен для всех сценариев, лишний сброс поведения может сделать последующие проверки некорректными.
В примере первый сброс удаляет только историю вызовов, поэтому возвращаемое значение сохраняется. Второй сброс удаляет и настроенное return_value; новый результат создаётся уже самим моком.
На практике для независимых тестов обычно лучше создавать новый мок в каждой фикстуре или в каждом тесте. reset_mock полезен внутри одного теста для последовательности фаз либо при контролируемом переиспользовании дорогой или сложной подмены, но он не заменяет изоляцию тестов.
Тест проверяет повторную отправку сообщения. В первой фазе мок клиента вызывается при первоначальной отправке, затем тест очищает историю и запускает повторную отправку. Вызвать reset_mock() здесь разумно: конфигурация клиента должна остаться прежней, а проверять нужно только взаимодействия второй фазы.
Можно создать новый мок для второй фазы. Это лучше изолирует состояния, но требует повторить настройку и может усложнить тест, если у мока много связанных дочерних объектов. Можно использовать reset_mock(return_value=True, side_effect=True), однако это удалит поведение, нужное второй фазе.
Оптимальное решение — обычный reset_mock(), если меняется только граница наблюдения, и явная перенастройка после сброса, если должен измениться сценарий поведения. Такой тест сохраняет независимость проверок и не скрывает, какие настройки действительно общие.
reset_mock() настройки дочерних моков?Да, история вызовов дочерних моков сбрасывается рекурсивно. Например, обращения к service.client.send будут удалены из истории соответствующего дочернего объекта. Но его return_value, side_effect и прочие настройки по умолчанию сохраняются.
reset_mock() отличается от создания нового Mock?Новый мок не имеет ни истории, ни настроек, ни созданных ранее атрибутов. reset_mock() сохраняет структуру и конфигурацию объекта, очищая главным образом состояние взаимодействий. Поэтому новый мок обеспечивает более сильную изоляцию, а сброс удобен, когда поведение подмены должно остаться тем же.
reset_mock()?Не всегда. История вызовов будет очищена, но произвольные изменения атрибутов, настройки поведения и внешние эффекты самой тестируемой системы могут сохраниться. Для независимых тестов предпочтительна новая фикстура с областью действия на функцию; reset_mock() подходит для отдельных фаз внутри одного контролируемого сценария.