Python Mox: Как подделать os.path.exists() только для определенных путей?
Как я могу издеваться exists()
только для определенных путей, когда он делает реальную вещь для любого другого пути?
Например, тестируемый класс вызовет exists()
и потерпит неудачу на путях, которые были ему предоставлены, потому что они не существуют в системе, где выполняются тесты.
С Mox можно было бы полностью заглушить exists()
, но это может сделать тест неудачным, потому что вызовы, не относящиеся к тестируемому классу, не будут действовать реальным образом.
Я думаю, я мог бы использовать WithSideEffects()
вызывать мою собственную функцию, когда exists()
называется ответвление вызова в двух направлениях, но как я могу получить доступ к оригиналу exists()
?
Это то, что я до сих пор:
def test_with_os_path_exists_partially_mocked(self):
self.mox.StubOutWithMock(os.path, 'exists')
def exists(path):
if not re.match("^/test-path.*$", path):
return call_original_exists_somehow(path)
else:
# /test-path should always exist
return True
os.path.exists(mox.Regex("^.*$")).MultipleTimes().WithSideEffects(exists)
self.mox.ReplayAll()
under_test.run()
self.mox.VerifyAll()
1 ответ
Mox внутренне использует "Stubout" для фактической заглушки:
Mox.__init__()
self._mock_objects = []
self.stubs = stubout.StubOutForTesting()
Mox.StubOutWithMock()
...
self.stubs.Set(obj, attr_name, stub)
Stubout сохраняет заглушку во внутренней коллекции:
StubOutForTesting.Set():
...
self.cache.append((parent, old_child, child_name))
Поскольку возвращаемое значение промежуточных вызовов кэшируется в объекте mocked-метода, оно должно быть сброшено при обратном вызове побочного эффекта.
Когда создается макет метода, он будет помещен в _expected_calls_queue
, В режиме воспроизведения ожидаемый вызов повторяется MultipleTimesGroup
экземпляр, который будет отслеживать вызовы каждого метода, указанного в _methods
,
Таким образом, можно обратиться к оригинальному методу, перемещаясь Mox.stubs.cache
,
Этот пример будет издеваться exists()
Прохождение вызовов исходной функции, если они не начинаются с /test-path
, любой другой звонок всегда вернется True
,
class SomeTest(unittest.TestCase):
def setUp(self):
self.mox = mox.Mox()
def tearDown(self):
self.mox.UnsetStubs()
def test_with_os_path_exists_partially_mocked(self):
self.mox.StubOutWithMock(os.path, 'exists')
# local reference to Mox
mox_ = self.mox
# fake callback
def exists(path):
# reset returnvalues of previous calls
# iterate mocked methods. the indices denote
# the mocked object and method and should
# have the correct values
for method in mox_._mock_objects[0]._expected_calls_queue[0]._methods:
method._return_value = None
if not re.match("^/test-path.*$", path):
# call real exists() for all paths not
# starting with /test-path
# lookup original method:
# - filter by name only (simplest matching)
# - take the 2nd value in the tupel (the function)
orig_exists = filter(lambda x: x[2] == "exists", mox_.stubs.cache)[0][1]
# call it
return orig_exists(path)
else:
# hardcoded True for paths starting with /test-path
return True
# expect call with any argument, multiple times, and call above fake
os.path.exists(mox.Regex("^.*$")).MultipleTimes().WithSideEffects(exists)
self.mox.ReplayAll()
# test goes here
self.mox.VerifyAll()