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()

person try-catch-finally    schedule 22.02.2015    source источник


Ответы (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))

Поскольку возвращаемое значение промежуточных вызовов кэшируется в фиктивном объекте метода, его необходимо сбросить в обратном вызове побочного эффекта.

Когда создается макет метода, он будет сохранен в _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()
person Community    schedule 22.02.2015