I am trying to mock a class which is called in a module which imports said class, which I want to test.
# application.py
from my_module.my_submodule import MyClass
def my_function(var1):
instance = MyClass()
instance.some_function(var1)
and my testing file
# test_application.py
import mock
import application
def test_my_function():
with mock.patch('my_module.my_submodule.MyClass') as MockClass:
application.my_function(var1)
MockClass.assert_called()
This gives an error saying MockClass was not called.
Now, by looking at this question: Why python mock patch doesn't work?, I was inspired to change the application.py import to this
# application.py
import my_module.my_submodule as mysub
def my_function(var1):
instance = mysub.MyClass()
instance.some_function(var1)
that is, I don't directly import the class that I want to mock in the test. Now it works.
My question is, if this is working as intended, or I am doing something wrong in the original way? Is it really necessary to always import modules like this, if I want to mock a class used in a module I want to test?
Yes it is working as intended, but you patched the wrong target.
Try patching application.MyClass.
application is not using my_module.my_submodule.MyClass anywhere,
but MyClass alias instead.
No, you don't have to import modules in some specific way to be able to mock/patch some name. What you have to do is to look at how that name is used at runtime, when you want it patched.
If the test is in a separate module from the module being tested, and the tested module, application in this case, imports the name and uses it directly like in from a import MyClass, then in the test module you import application and patch application.MyClass. If instead application uses import a and then calls a.MyClass() you have to patch application.a.MyClass.
So you adapt the patch target to the concrete naming scenario in the application module. For example if your test is in the same application module, and the class is being used as MyClass, you need to patch __main__.MyClass.
It is however true, that writing code in certain ways can make it easier to patch when you are testing. A good example is when the entity to mock is a function parameter. Just call the function with a mock argument.
If you find that patching is too convoluted or looks impossible, try to rewrite the code in the application so that it is more "testable".
For another example as reference, see Where to patch
Related
I have what should've been a simple task, and it has stumped me for a while. I am trying to patch an object imported into the current module.
Per the answers to Mock patching from/import statement in Python
I should just be able to patch("__main__.imported_obj"). However, this isn't working for me. Please see my below minimal repro (I am running the tests via pytest):
Minimal Repro
This is run using Python 3.8.6.
from random import random
from unittest.mock import patch
import pytest
#pytest.fixture
def foo():
with patch("__main__.random"):
return
def test(foo) -> None:
pass
When I run this code using PyCharm, I get an AttributeError:
AttributeError: <module '__main__' from '/Applications/PyCharm.app/Contents/plugins/python/helpers/pycharm/_jb_pytest_runner.py'> does not have the attribute 'random'
Furthermore, when I enter debugger mode in the line before the with patch, I see the attribute __main__ is not defined. I am not sure if it needs to be defined for patch to work its magic.
NOTE: I know I can use patch.object and it becomes much easier. However, I am trying to figure out how to use patch in this question.
Research
Unable to mock open, even when using the example from the documentation
This question is related because it's both a similar error message and use case. Their solution was to use builtins instead of __main__, but that's because they were trying to patch a built-in function (open).
You are assuming that the module the test is running in is __main__, but that would only be the case if it were called via main. This is usually the case if you are using unittest. With pytest, the tests live in the module they are defined in.
You have to patch the current module, the name of which is accessible via __name__, instead of assuming a specific module name:
from random import random
from unittest.mock import patch
import pytest
#pytest.fixture
def foo():
with patch(__name__ + ".random"):
yield
In the following question the function that uses the ftplib is defined in the same file, which makes it trivial to patch ('ftplib.FTP')
Mocking ftplib.FTP for unit testing Python code
My question is: How should I proceed if, in my test, I would like to make an instance of a class (let's call it 'A') that uses the ftplib somewhere (ie: the class A has an instance of a class B and B has an FTP object which calls connect()) ?
import unittest
from mock import patch
class TestClass(unittest.TestCase):
#patch(???)
def test_1(self, mock_ftp_constructor):
mock_ftp = mock_ftp_constructor.return_value
a = A()
self.assertTrue(mock_ftp.connect.called)
if __name__ == "__main__":
unittest.main()
If I use the solution given in the other question, I find that the ftplib is called instead of the mock. How could I know the correct path of ftplib.FTP ?
When dealing with imports to mock a class, it matters the way how the file (where the class is) has imported the lib.
To make the patch it is necessary to know the name of the context where the lib is called.
If __name__ is 'A.B', patch would be: #patch('A.B.ftplib.FTP') if B imports FTP as import ftplib.
If B imports the lib as from ftplib import FTP, it would be: #patch('A.B.FTP')
During a test phase, I would like to modify a part of my source code. For instance, I don't need to plot, so the code responsible for plotting can be commented. Is there a proper tool in unittest module to do so?
Another question is regarding the decorator #patch. Is there a way I can put them into source code in runtime during testing?
Try working with mock,
As it sounds it mocks your code and can be manipulated thru the test.
You may mock an method returned val or an object instance etc etc.
https://www.toptal.com/python/an-introduction-to-mocking-in-python
As Ohad the Lad already said, mocks are at your help.
There are several ways how to mock a function, but in general, you will decorate your test-functions - not the function to mock!
In your case the code could look as follows:
# your_file.py
from ... import ... as plt #depends on the class you use
class MyPlotter(object):
def draw_calling_method(self):
....
plt.draw()
...
return something
# test.py
import mock
from unittest import TestCase
from your_file import MyPlotter
from ... import ... as plt # same import as in the your_file.py
class TestMyPlotter(TestCase):
#mock.patch.object(plt, 'draw')
def test_draw_calling_method_returns_something(self, draw):
plotter = MyPlotter()
plotter_return = plotter.draw_calling_method()
self.assertEqual(plotter_return, something)
This will replace all calls to the plt.draw() with MagicMocks and hinder the draw-execution. If all of your test-methods need to mock the draw-call, the mock decorator could also be applied to the class instead. You only have to make sure then, that all your test-methods accept the mock as second argument (as the test_draw_calling_mehtod_returns_something does). Further you could mock any return values the draw() function may have by setting draw.return_value = ... in the test-method. Attention: This has to happen prior to the call of the function which calls the mock, otherwise the return-values will not be applied.
For more information on mocking refer to the docs python-docs. They are quite comprehensive.
One last thing; As Jonathon Reinhart already mentioned, if you feel it difficult to write your tests it may be good to refactor your code before. This will not only make it more testable, but more readable as well!
I am currently writing test coverage for a wrapper around sqlite3 in Python. At present my code looks something like this:
import unittest
from unittest.mock import patch
from MemberDatabase import MemberDatabase
#patch('sqlite3.connect')
class MemberDatabaseTestCase(unittest.TestCase):
def test_dbConnect(self, mocksql_connect):
mdb = MemberDatabase('test.db')
mocksql_connect.assert_called_with('test.db')
def test_optionalCommit(self, mocksql_connect):
mdb = MemberDatabase('test.db')
mdb.optionalCommit()
self.assertTrue(mocksql_connect().commit.called)
Various other tests follow which all create mdb in the same way. The constructor of MemberDatabase calls sqlite3.connect(). I would like to tidy the test case up by moving the assignment of mdb to setUp(). However, sqlite3.connect() isn't mocked in setUp() so this results in the actual sqlite3.connect() being called and a real database connection being created.
Is there any way to use the patched sqlite3.connect() in setUp()? Perhaps I need to create the mock inside setUp() instead of using the decorator, but I think the code is cleaner using the decorator approach. Also, I read that if I create mock
I'm writing a Python library which is meant to be used as a third party library.
great_library/__init__.py:
class ClassA(object):
#cache()
def foo(self):
pass
class ClassB(object):
#cache()
def bar(self):
pass
class GreatLibrary(object):
#classmethod
def great_api(cls):
pass
# uses ClassA and ClassB
this library is used as:
from great_library import GreatLibrary
GreatLibrary.great_api()
Now the problem is, I'd like the user to config cache expiration time. which should be passed to #cache(): #cache(seconds)
How should I design this module structure so the user could easily pass in the config and let it be used by classA and classB ? thanks
The base problem is that the variable passed to the decorator will be read when the module will be load. So there are no way to change it before (at least if you don't want to reload the module by some hacking but that cannot change the old objects). So you need some hook where great_library can get the value of the cache time and where the user can write the desired value.
The more simple and wide used method is set environment variables. At the top of your great_library module you can check the variables and load the default cache time:
import os
default_time = os.getenv("GREAT_LIBRARY_CACHE_TIME", None)
In your code use #cache(default_time). I'm not sure that the cache() API take None as default argument, otherwise is simple to modify the receipt to adapt it to your problem.
Now the users of great_library can be set it either by os.putenv() in devolopment stage (before import the module) or by OS environment in production.
An other way to put an hook can be use a configuration module to import. IMHO that method can be useful only if you have a bunch of property to set. If you follow that path your great_library module should implement something of this:
try:
from great_library_config import *
except ImportError:
# Default configurations like...
default_time = None
Personally I try to avoid solutions like that for a module but can be useful for applications or framework with an high degree of configurability. Anyway also in that case the user can use a config module for production and override it by a developing/testing one.