Строгий макет в питоне
Есть ли какой-нибудь эквивалент строгих издевательств в питоне? Некоторый механизм для сообщения о непреднамеренном вызове фиктивных методов (action.step2() в этом примере), как в GoogleMock Framework.
class Action:
def step1(self, arg):
return False
def step2(self, arg):
return False
def algorithm(action):
action.step1('111')
action.step2('222')
return True
class TestAlgorithm(unittest.TestCase):
def test_algorithm(self):
actionMock = mock.create_autospec(Action)
self.assertTrue(algorithm(actionMock))
actionMock.step1.assert_called_once_with('111')
3 ответа
Похоже, это не поддерживается из коробки. Однако есть как минимум два подхода к достижению того же результата.
Проходной список разрешенных участников
Согласно макету документации
spec: это может быть либо список строк, либо существующий объект (класс или экземпляр), который действует как спецификация для фиктивного объекта. Если вы передаете объект, то список строк формируется путем вызова dir для объекта (исключая неподдерживаемые магические атрибуты и методы). Доступ к любому атрибуту, отсутствующему в этом списке, вызовет AttributeError.
Итак, для того, чтобы провалить тестовый пример, просто замените
actionMock = mock.create_autospec(Action)
в
actionMock = mock.Mock(spec=['step1'])
Такой подход имеет определенные недостатки по сравнению с прохождением класса или экземпляра, как spec
аргумент, так как вы должны передать все разрешенные методы и затем установить ожидания для них, эффективно регистрируя их дважды. Также, если вам нужно ограничить подмножество методов, вы должны передать список всех методов, кроме тех. Это может быть достигнуто следующим образом:
all_members = dir(Action) # according to docs this is what's happening behind the scenes
all_members.remove('step2') # remove all unwanted methods
actionMock = mock.Mock(spec=all_members)
Установка исключений для ограниченных методов
Альтернативный подход состоит в том, чтобы явно устанавливать ошибки для методов, которые вы не хотите вызывать:
def test_algorithm(self):
actionMock = mock.create_autospec(Action)
actionMock.step2.side_effect = AttributeError("Called step2") # <<< like this
self.assertTrue(algorithm(actionMock))
actionMock.step1.assert_called_once_with('111')
Это также имеет некоторые ограничения: вы должны установить ошибки, а также ожидания.
В качестве последнего замечания, одно радикальное решение проблемы будет патч mock
добавить strict
параметр для Mock
конструктор и отправить запрос на извлечение. Чем либо это будет принято, либо mock
сопровождающие укажут, как этого добиться.:)
Да, это возможно с помощью spec=
а также autospec=
аргументы. См. Ложную документацию по Autospeccing для получения дополнительной информации. В вашем примере это станет:
action_mock = mock.Mock(spec=Action)
или же:
action_mock = mock.Mock('Action', autospec=True)
Другая возможность:
Проверка call_count индивидуально на ограниченных методах
Убедиться, что call_count
ноль для методов, которые не должны вызываться.
class TestAlgorithm(unittest.TestCase):
def test_algorithm(self):
actionMock = mock.create_autospec(Action)
self.assertTrue(algorithm(actionMock))
actionMock.step1.assert_called_once_with('111')
self.assertEqual(actionMock.step2.call_count, 0) # <<< like this
Недостатком является то, что вы должны проверять все неожиданные звонки один за другим.