How to inject mock objects into instance attributes in Python unit testing

Question:

I am learning python

I’m wondering if there is a mechanism to “inject” an object (a fake object in my case) into the class under test without explicitly adding it in the costructor/setter.

## source file
class MyBusinessClass():
    def __init__(self):
        self.__engine = RepperEngine()

    def doSomething(self):
        ## bla bla ...
        success

## test file
## fake I'd like to inkject
class MyBusinessClassFake():
   def __init__(self):
      pass

def myPrint(self) :
    print ("Hello from Mock !!!!")

class Test(unittest.TestCase):

    ## is there an automatic mechanism to inject MyBusinessClassFake 
    ## into MyBusinessClass without costructor/setter?
    def test_XXXXX_whenYYYYYY(self):

        unit = MyBusinessClass()
        unit.doSomething()
        self.assertTrue(.....)

in my test I’d like to “inject” the object “engine” without passing it in the costructor. I’ve tried few option (e.g.: @patch …) without success.

Asked By: Kasper

||

Answers:

Your question seems somewhat unclear, but there is nothing preventing you from using class inheritance to override the original methods. In that case the derivative class would just look like this:

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):
      pass
Answered By: Noi Sek

IOC is not needed in Python. Here’s a Pythonic approach.

class MyBusinessClass(object):
    def __init__(self, engine=None):
        self._engine = engine or RepperEngine() 
        # Note: _engine doesn't exist until constructor is called.

    def doSomething(self):
        ## bla bla ...
        success

class Test(unittest.TestCase):

    def test_XXXXX_whenYYYYYY(self):
        mock_engine = mock.create_autospec(RepperEngine)
        unit = MyBusinessClass(mock_engine)
        unit.doSomething()
        self.assertTrue(.....)

You could also stub out the class to bypass the constuctor

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):  # we bypass super's init here
      self._engine = None

Then in your setup

def setUp(self):
  self.helper = MyBusinessClassFake()

Now in your test you can use a context manager.

def test_XXXXX_whenYYYYYY(self):
  with mock.patch.object(self.helper, '_engine', autospec=True) as mock_eng:
     ...

If you want to inject it with out using the constuctor then you can add it as a class attribute.

class MyBusinessClass():
    _engine = None
    def __init__(self):
        self._engine = RepperEngine() 

Now stub to bypass __init__:

class MyBusinessClassFake(MyBusinessClass):
   def __init__(self):
      pass

Now you can simply assign the value:

unit = MyBusinessClassFake()
unit._engine = mock.create_autospec(RepperEngine)
Answered By: Dan

After years using Python without any DI autowiring framework and Java with Spring I’ve come to realize plain simple Python code often doesn’t need frameworks for dependency injection without autowiring (autowiring is what Guice and Spring both do in Java), i.e., just doing something like this is enough:

def foo(dep = None):  # great for unit testing!
    self.dep = dep or Dep()  # callers can not care about this too
    ...

This is pure dependency injection (quite simple) but without magical frameworks for automatically injecting them for you (i.e., autowiring) and without Inversion of Control.

Unlike @Dan I disagree that Python doesn’t need IoC: Inversion of Control is a simple concept that the framework takes away control of something, often to provide abstraction and take away boilerplate code. When you use template classes this is IoC. If IoC is good or bad it is totally up to how the framework implements it.

Said that, Dependency Injection is a simple concept that doesn’t require IoC. Autowiring DI does.

As I dealt with bigger applications the simplistic approach wasn’t cutting it anymore: there was too much boilerplate code and the key advantage of DI was missing: to change implementation of something once and have it reflected in all classes that depend on it. If many pieces of your application cares on how to initialize a certain dependency and you change this initialization or wants to change classes you would have to go piece by piece changing it. With an DI framework that would be way easier.

So I’ve come up with injectable a micro-framework that wouldn’t feel non-pythonic and yet would provide first class dependency injection autowiring.

Under the motto Dependency Injection for Humans™ this is what it looks like:

# some_service.py
class SomeService:
    @autowired
    def __init__(
        self,
        database: Autowired(Database),
        message_brokers: Autowired(List[Broker]),
    ):
        pending = database.retrieve_pending_messages()
        for broker in message_brokers:
            broker.send_pending(pending)
# database.py
@injectable
class Database:
    ...
# message_broker.py
class MessageBroker(ABC):
    def send_pending(messages):
        ...
# kafka_producer.py
@injectable
class KafkaProducer(MessageBroker):
    ...
# sqs_producer.py
@injectable
class SQSProducer(MessageBroker):
    ...

Be explicit, use a setter. Why not?

class MyBusinessClass:
    def __init__(self):
        self._engine = RepperEngine()
from unittest.mock import create_autospec

def test_something():
  mock_engine = create_autospec(RepperEngine, instance=True)
  object_under_test = MyBusinessClass()
  object_under_test._engine = mock_engine

Answered By: Keto
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.