Как правильно смоделировать зависимости, введенные в другой класс с python? - PullRequest
0 голосов
/ 26 марта 2020

Я пытаюсь понять способы работы с макетами и python.

class B:
    def foo(self, value):
        return value


class A:
    def __init__(self, b: B):
        self._b = b

    def bar(self, value):
        return self._b.foo(value)

Итак, простая зависимость, где A зависит от B, который вводится через конструктор.

Мой простой тест

class TestX(unittest.TestCase):

    @patch.object(B, 'foo')
    def test_it_should_return_same_value(self, mock_b):
        value = 'X'
        mock_b.return_value = value
        a = A(mock_b)
        self.assertEqual(a.bar(value), value)
        mock_b.assert_called_with(value)

Что не получается с AssertionError: <MagicMock name='foo.foo()' id='140112335838224'> != 'X

Есть идеи, почему?

Ответы [ 2 ]

0 голосов
/ 18 апреля 2020

Я думаю, вы путаете b с b.foo. Вы смоделировали метод b.foo(), а затем использовали фиктивный объект в качестве параметра B объекта для A.__init(). Вот ваш пример с исправленной ошибкой. Я также использовал разные значения для входного значения и возвращаемого значения, чтобы не перепутать их.

import unittest
from unittest.mock import patch


class B:
    def foo(self, value):
        return value


class A:
    def __init__(self, b: B):
        self._b = b

    def bar(self, value):
        return self._b.foo(value)


class TestX(unittest.TestCase):

    @patch.object(B, 'foo')
    def test_it_should_return_same_value(self, mock_b_foo):
        value_in = 'X'
        value_out = 'Y'
        mock_b_foo.return_value = value_out
        b = B()
        a = A(b)
        self.assertEqual(a.bar(value_in), value_out)
        mock_b_foo.assert_called_with(value_in)
0 голосов
/ 10 апреля 2020

С моей точки зрения, в Python есть хороший способ работы с макетами, используя 2 концепции:

  1. Python Множественное наследование для разработки ваших классов
  2. Использование из super () и Python Порядка разрешения методов (MRO) для внедрения mock для зависимостей классов в тестовом коде.

Что касается первого пункта, ваши классы будут выглядеть так:

class B:
    def foo(self, value):
        return value


class A(B):
    def bar(self, value):
        return super().foo(value)

import unittest

class MockB(B):
    def __init__(self):
        self.value = None

    def foo(self, value):
        return self.value

    def set_b_response(self, value):
        self.value = value

class ASut(A, MockB):
    'Injecting mock in A dependency'

class TestA(unittest.TestCase):

    def test_it_should_return_same_value(self):
        value = 'X'
        a = ASut()
        a.set_b_response(value)        

        self.assertEqual(a.bar(value), value)

Видя MRO наших классов SUT, мы можем понять, почему множественное наследование и использование super () позволяют нам вводить mock таким образом.

Результирующее MRO для BotSut :

Help on class ASut in module __main__:

class ASut(A, MockB)
 |  Injecting mock in A dependency
 |  
 |  Method resolution order:
 |      ASut
 |      A
 |      MockB
 |      B
 |      builtins.object

Для получения дополнительной информации:

...