Вы идете за борт. Вы тестируете реализацию, а не результат. Более того, вы попадаете во внутреннюю часть фиктивной реализации, к которой вам не нужно прикасаться.
Проверьте, что вы получите правильный результат, и проверьте, что результат основан на входах, которые вы хотите использовать. Вы можете настроить макет так, чтобы round()
передавалось фактическое числовое значение в раунд:
x.a + x.b
приводит к вызову m.a.__add__
с передачей m.b
.
m.a.__add__().c()
вызывается, поэтому мы можем проверить, вызван ли он, если это необходимо.
- Просто установите результат
c()
на число, чтобы round()
округлилось. Получение правильного round(number)
результата из функции означает, что .c()
был вызван.
Здесь достаточно ввести число к round()
, потому что вы не тестируете функцию round()
. Вы можете положиться на сопровождающих Python, чтобы протестировать эту функцию, сосредоточившись на тестировании собственного кода.
Вот что я бы протестировал:
m = unittest.mock.MagicMock()
# set a return value for (x.a + *something*).c()
mock_c = m.a.__add__.return_value.c
mock_c.return_value = 42.4
r = to_be_tested(m)
mock_c.assert_called_once()
self.assertEqual(r, 42)
Если вы должны утверждать, что m.a + m.b
имел место, то вы можете добавить
m.a.__add__.assert_called_once(m.b)
, но передача подтверждения вызова mock_c
уже является доказательством того, что по крайней мере выражение (m.a + <whatever>)
имело место и что к результату был получен доступ c
.
Если вы должны подтвердить, что round()
использовался на фактическом макете, вам придется придерживаться исправления класса MagicMock
, чтобы включить __round__
в качестве специального метода, и удалить mock_c.return_value
присваивание, после которого вы можете утверждать, что возвращаемое значение является правильным объектом с
# assert that the result of the `.c()` call has been passed to the
# round() function (which returns the result of `.__round__()`).
self.assertIs(r, mock_c.return_value.__round__.return_value)
Некоторые дополнительные примечания:
- Нет смысла пытаться сделать все фиктивным объектом. Если тестируемый код должен работать со стандартными типами Python, просто сделайте, чтобы ваши mocks создавали эти типы. Например. если ожидается, что какой-то вызов выдаст строку, ваш макет вернет тестовую строку, особенно когда вы затем передаете материал в другие API стандартной библиотеки.
- издевается над одиночками. Вам не нужно возвращаться из данного макета, чтобы проверить, что у них есть правильный родительский элемент, потому что вы можете добраться до того же объекта, пройдя через родительские атрибуты, а затем использовать
is
. Например. если функция возвращает где-то фиктивный объект, вы можете утверждать, что фиктивный объект right был возвращен путем тестирования assertIs(mock_object.some.access.return_value.path, returned_object)
.
- Когда издевается, этот факт записывается. Вы можете подтвердить это с помощью методов
assert_called*
, атрибутов .called
и .call_count
и просмотреть результаты вызовов с атрибутами .return_value
В случае сомнений осмотрите атрибут .mock_calls
, чтобы увидеть, к какому доступу тестируется код. Или сделать это в интерактивном сеансе. Например, проще увидеть, что m.a + m.b
делает в быстром тесте с:
>>> from unittest import mock
>>> m = mock.MagicMock()
>>> m.a + m.b
<MagicMock name='mock.a.__add__()' id='4495452648'>
>>> m.mock_calls
[call.a.__add__(<MagicMock name='mock.b' id='4495427568'>)]