Как смоделировать функцию, вызванную в классе "A" и определенную в классе "B"? - PullRequest
0 голосов
/ 29 мая 2019

Я пишу код модульного теста для проверки функции «a», определенной в классе A, которая вызывает функцию «b», определенную в классе «B». Как сделать так, чтобы «а» вызывал функцию b-mocking?

#-------------------------------------------------
class B :

    def __init__(self):
        print('hello B')

    def b(self):
        return False

#------------------------------------------------
class A :
    def __init__(self):
        print('hello A')


    def a(self,a,b):
        ret = B().b()
        if ret == True:
            return 0
        else :
            return a+b
#-----------------------------------------------
class TestA(unittest.TestCase):

    def setUp(self):
        pass

    def return_true(self):
        return True

    @patch('__main__.B')
    def test_a_in_A(self,mocked_B):
        mocked_B.b.return_value = True
        assert A().a(2,3) == 0

#---------------------------------------------
if __name__ == '__main__':
    unittest.main()


я ожидал, что подтвердит A (). A (2,3) == 0 будет test OK но фактический результат составляет AssertionError

Примечание: когда я запускаю assert A (). A (2,3) == 5 , тест возвращает test OK, что означает, что макет не работает правильно.

1 Ответ

1 голос
/ 29 мая 2019

Хорошо, длинная версия: издевательство - это когда вы заменяете что-то упрощенной формой.Тем не менее, вы не можете издеваться над тем, что не было использовано.Это не имеет никакого смысла.

Давайте посмотрим на ваш код здесь:

def a(self,a,b):
    ret = B().b()
    if ret == True:
        return 0
    else :
        return a+b

Вы создаете новый B () внутри этой функции, поэтому он не имеет return_valueВы устанавливаете в другом месте в тесте на mocked_B.Весь смысл наличия фиктивного объекта в том, что вы передаете имитатор и получаете его предсказуемое поведение.Вы не можете смоделировать объект, который полностью инкапсулирован внутри тестируемой функции.

Здесь я изменил ваш код, чтобы взять (возможно, смоделированный) объект B.Я переименовал целочисленные аргументы из a, b в x, y, потому что это было слишком запутанным.Они не имеют ничего общего с A или B.

Также вы забыли некоторые операции импорта.

import unittest
from unittest.mock import patch

class B :
    def __init__(self):
        print('hello B')

    def b(self):
        return False

class A :
    def __init__(self):
        print('hello A')

    def a(self, x, y, b):
        if b.b():
            return 0
        else:
            return x + y

class TestA(unittest.TestCase):
    def setUp(self):
        pass

    def return_true(self):
        return True

    @patch('__main__.B')
    def test_a_in_A(self,mocked_B):
        mocked_B.b.return_value = True
        assert A().a(2,3, mocked_B) == 0

if __name__ == '__main__':
    unittest.main()

В соответствии с запросом приведена пересмотренная версия с меньшим влиянием на существующий код:

class A :
    def __init__(self, b=None):
        # this instance can use a member instance of B
        self.b = b if b else B()
        print('hello A')

    def a(self, x, y):
        if self.b.b() is True:
            return 0
        else:
            return x + y

Я изменил тест на «self.bb () - True», чтобы избежать запуска в объекте MagicMock без инициализации return_value, но, тем не менее, оценивается как true, потому что ... это экземпляр, и вот как bool (object)работает.

Здесь вы все равно должны передать в макет экземпляр, в котором значение return_value установлено на то, что вы хотите, поэтому тест будет

assert A(mocked_B).a(2,3) == 0

Вам нужно что-то заменить,если вы хотите, чтобы фиктивный объект что-то заменил.Это может помочь найти «внедрение зависимостей», которое является еще одним названием той же основной идеи.

...