2


I have a class representing our DB layer which is being instantiated internally in some classes (I cannot pass it as an outside parameter)
For example:

class MyClass(object):
    def __init__(self):
        self.dbapi = DatabaseAPI()
        self.timeout = 120

    def some_methods(self):
        pass

We're writing some Unit tests, and we'd like to mock the self.dbapi with an existing instance which we'll be creating before the test runs.

for example:

my_dbapi = DatabaseAPIMock()
...
...
@patch('MyModule.DatabaseAPI', my_dbapi)
def my_test(self):
    my_class = MyClass() #<---This is where I'm not able to mock the DatabaseAPI

This is what I tried to achieve so far, but from debugging the code I see that the self.dbapi is instantiated with the real object and not with the pre-made mock.

What am I missing?
BTW, we're running python 2.7

Thanks in advance!

2
  • Can you explain how you import DatabaseAPI in MyModule? Commented Mar 25, 2015 at 13:17
  • I've completely changed my answer. Commented Mar 25, 2015 at 13:45

2 Answers 2

2

You're patching the wrong thing. You need to patch in the module that is assigning the attribute, ie the one that contains the target class.

It would be easier if you defined a method in your target class that gets your DatabaseAPI object. That way you can much more easily patch it.

class MyClass(object):
    def __init__(self):
        self.dbapi = self.get_db_api()
        self.timeout = 120

    def get_db_api():
        return DatabaseAPI()

and the test becomes:

@patch('my_module.MyClass.get_db_api')
def my_test(self, my_method):
    my_method.return_value = my_dbapi
Sign up to request clarification or add additional context in comments.

3 Comments

HI Daniel, Thanks for the quick response, It's my fault that I 'skipped' that part, but in my code I actually patch the full class path, including modules. I'll update the question
Regarding your second recommendation, We have few hundred of classes written in a similar way to MyClass. I'm trying to create a single patch decorator that will suit all of them without needing to create a different patch for each test (as the class name changes). Also, I'd rather not to change the code of those classes unless there won't be any other way.
I have a similar scenario. I can not change the target class and it internally instantiates a utility class instance, which although can be mocked in the testing class it does not affect what is used inside the target class. So the above approach does not work
0

Short answer: use

@patch('MyModule.DatabaseAPI', return_value=my_dbapi)

instead.

In MyModule DatabaseAPI is something like a factory but my_dbapi is a instance. So by set the instance as return value of your factory you mock self.dbapi reference.

If in production code you can change the design a little bit consider to move as @DanielRoseman's answer propose: it is a better design.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.