I need to create some kind of MockMixin for my tests. It should include mocks for everything that calls external sources.
For example, each time I save model in admin panel I call some remote URLs. It would be good, to have that mocked and use like that:
class ExampleTestCase(MockedTestCase):
# tests
So each time I save model in admin, for example in functional tests, this mock is applied instead of calling remote URLs.
Is that actually possible? I'm able to do that for 1 particular test, that is not a problem. But it'd be more useful to have some global mock because I use it a lot.
According to the mock documentation:
Patch can be used as a TestCase class decorator. It works by
decorating each test method in the class. This reduces the boilerplate
code when your test methods share a common patchings set.
This basically means that you can create a base test class with #patch decorator applied on it that would mock your external calls while every test method inside would be executed.
Also, you can use start() and stop() patcher's methods in setUp() and tearDown() methods respectively:
class BaseTestCase(TestCase):
def setUp(self):
self.patcher = patch('mymodule.foo')
self.mock_foo = self.patcher.start()
def tearDown(self):
self.patcher.stop()
Just to add to alecxe's answer, if you are using teardown() then according to the docs
you must ensure that the patching is “undone” by calling stop. This can be fiddlier than you might think, because if an exception is raised in the setUp then tearDown is not called.
If an exception is raised in your tests, your patching won't be undone. A better way would be to call addCleanup() inside your setUp(). Then you can omit the tearDown() method altogether.
class BaseTestCase(TestCase):
def setUp(self):
self.patcher = patch('mymodule.foo')
self.mock_foo = self.patcher.start()
self.addCleanup(self.patcher.stop) # add this line
I ended up creating a test runner to serve my purpose. I needed to mock the file storage so that images do not actually write to the file system while testing. The images object is being called in many tests thus patching each class would not be DRY. Also, I noticed that mocking the file itself would leave it on the system in case the test failed. But this method didn't.
I created a file runner.py in the project root
# runner.py
from unittest.mock import patch
from django.test.runner import DiscoverRunner
from myapp.factories import ImageFactory
class UnitTestRunner(DiscoverRunner):
#patch('django.core.files.storage.FileSystemStorage.save')
def run_tests(self, test_labels, mock_save, extra_tests=None, **kwargs):
mock_save.return_value = ImageFactory.get_image()
return super().run_tests(test_labels, extra_tests=None, **kwargs)
Then I would run my tests using python manage.py tests --testrunner=runner.UnitTestRunner
Just for clarity the ImageFactory.get_image method is a custom method
from django.core.files.base import ContentFile
from factory.django import DjangoModelFactory
from io import BytesIO
from PIL import Image as PilImage
from random import randint
class ImageFactory(DjangoModelFactory):
#classmethod
def get_image(cls, name='trial', extension='png', size=None):
if size is None:
width = randint(20, 1000)
height = randint(20, 1000)
size = (width, height)
color = (256, 0, 0)
file_obj = BytesIO()
image = PilImage.new("RGBA", size=size, color=color)
image.save(file_obj, extension)
file_obj.seek(0)
return ContentFile(file_obj.read(), f'{name}.{extension}')
Related
I have a database handler that utilizes SQLAlchemy ORM to communicate with a database. As part of SQLAlchemy's recommended practices, I interact with the session by using it as a context manager. How can I test what a function called inside the context manager using that context manager has done?
EDIT: I realized the file structure mattered due to the complexity in introduced. I re-structured the code below to more closely mirror what the end file structure will be like, and what a common production repo in my environment would look like, with code being defined in one file and tests in a completely separate file.
For example:
Code File (delete_things_from_table.py):
from db_handler import delete, SomeTable
def delete_stuff(handler):
stmt = delete(SomeTable)
with handler.Session.begin() as session:
session.execute(stmt)
session.commit()
Test File:
import pytest
import delete_things_from_table as dlt
from db_handler import Handler
def test_delete_stuff():
handler = db_handler()
dlt.delete_stuff(handler):
# Test that session.execute was called
# Test the value of 'stmt'
# Test that session.commit was called
I am not looking for a solution specific to SQLAlchemy; I am only utilizing this to highlight what I want to test within a context manager, and any strategies for testing context managers are welcome.
After sleeping on it, I came up with a solution. I'd love additional/less complex solutions if there are any available, but this works:
import pytest
import delete_things_from_table as dlt
from db_handler import Handler
class MockSession:
def __init__(self):
self.execute_params = []
self.commit_called = False
def execute(self, *args, **kwargs):
self.execute_params.append(["call", args, kwargs])
return self
def commit(self):
self.commit_called = True
return self
def begin(self):
return self
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
pass
def test_delete_stuff(monkeypatch):
handler = db_handler()
# Parens in 'MockSession' below are Important, pass an instance not the class
monkeypatch.setattr(handler, Session, MockSession())
dlt.delete_stuff(handler):
# Test that session.execute was called
assert len(handler.Session.execute_params)
# Test the value of 'stmt'
assert str(handler.Session.execute_params[0][1][0]) == "DELETE FROM some_table"
# Test that session.commit was called
assert handler.Session.commit_called
Some key things to note:
I created a static mock instead of a MagicMock as it's easier to control the methods/data flow with a custom mock class
Since the SQLAlchemy session context manager requires a begin() to start the context, my mock class needed a begin. Returning self in begin allows us to test the values later.
context managers rely on on the magic methods __enter__ and __exit__ with the argument signatures you see above.
The mocked class contains mocked methods which alter instance variables allowing us to test later
This relies on monkeypatch (there are other ways I'm sure), but what's important to note is that when you pass your mock class you want to patch in an instance of the class and not the class itself. The parentheses make a world of difference.
I don't think it's an elegant solution, but it's working. I'll happily take any suggestions for improvement.
I'm working on unit tests for a service I made that uses confluent-kafka. The goal is to test successful function calls, exception errors, etc. The problem I'm running into is since I'm instantiating the client in the constructor of my service the tests are failing since I'm unsure how to patch a constructor. My question is how do I mock my service in order to properly test its functionality.
Example_Service.py:
from confluent_kafka.schema_registry import SchemaRegistryClient
class ExampleService:
def __init__(self, config):
self.service = SchemaRegistryClient(config)
def get_schema(self):
return self.service.get_schema()
Example_Service_tests.py
from unittest import mock
#mock.patch.object(SchemaRegistryClient, "get_schema")
def test_get_schema_success(mock_client):
schema_Id = ExampleService.get_schema()
mock_service.assert_called()
The problem is that you aren't creating an instance of ExampleService; __init__ never gets called.
You can avoid patching anything by allowing your class to accept a client maker as an argument (which can default to SchemaRegistryClient:
class ExampleService:
def __init__(self, config, *, client_factory=SchemaRegistryClient):
self.service = client_factory(config)
...
Then in your test, you can simply pass an appropriate stub as an argument:
def test_get_schema_success():
mock_client = Mock()
schema_Id = ExampleService(some_config, client_factory=mock_client)
mock_client.assert_called()
Two ways
mock entire class using #mock.patch(SchemaRegistryClient) OR
replace #mock.patch.object(SchemaRegistryClient, "get_schema") with
#mock.patch.object(SchemaRegistryClient, "__init__")
#mock.patch.object(SchemaRegistryClient, "get_schema")
I'd like to test a method, whether it calls a specific method of a temporary internal object or not. (ConfigParser.read)
So the object is created inside, and it's not accessible from the outside after the method exits.
Using python 2.7
In foobar.py
import ConfigParser
class FooBar:
def method(self, filename):
config=ConfigParser.ConfigParser()
config.read(filename)
do_some_stuff()
I'd like to test whether config.read was called.
As I understand, the patch decorator was made for this, but unfortunately the MagicMock object the testcase receives is not the same that is created inside, and I can't get near the object that lives inside the method.
I tried like this:
class TestFooBar(TestCase):
def setUp(self):
self.myfoobar = FooBar()
#mock.patch('foobar.ConfigParser')
def test_read(self,mock_foobar):
self.myfoobar.method("configuration.ini")
assert mock_foobar.called # THIS IS OKAY
assert mock_foobar.read.called # THIS FAILS
mock_foobar.read.assert_called_with("configuration.ini") # FAILS TOO
The problem is:
- mock_foobar is created before the self.myfoobar.method creates the ConfigReader inside.
- when debugging mock_foobar has internal data about the previous calls, but no "read" property (the inner MagicMock for mocking the read method)
Of course one way out is refactoring and giving the .read() or the init() a ConfigReader object, but it's not always possible to change the code, and I'd like to grasp the internal objects of the method without touching the module under test.
You're so close! The issue is that you are mocking the class, but then your test checks that read() is called on that mock class - but you actually expect read() to be called on the instance that is returned when you call the class. The following works - I find the second test more readable than the first, but they both work:
import ConfigParser
from unittest import TestCase
from mock import create_autospec, patch, Mock
class FooBar(object):
def method(self, filename):
config=ConfigParser.ConfigParser()
config.read(filename)
class TestFooBar(TestCase):
def setUp(self):
self.myfoobar = FooBar()
#patch('ConfigParser.ConfigParser')
def test_method(self, config_parser_class_mock):
config_parser_mock = config_parser_class_mock.return_value
self.myfoobar.method("configuration.ini")
config_parser_class_mock.assert_called_once_with()
config_parser_mock.read.assert_called_once_with("configuration.ini")
def test_method_better(self):
config_parser_mock = create_autospec(ConfigParser.ConfigParser, instance=True)
config_parser_class_mock = Mock(return_value=config_parser_mock)
with patch('ConfigParser.ConfigParser', config_parser_class_mock):
self.myfoobar.method("configuration.ini")
config_parser_class_mock.assert_called_once_with()
config_parser_mock.read.assert_called_once_with("configuration.ini")
I'm trying to run unit tests on a class that imports two other modules, and I'm trying to patch out those modules using mock. One of these modules is instantiated in the class I'm testing, and I have not been able to patch it out. It appears that I have managed to patch out the other one.
What is the best way to patch out the sequence.processor module in this code?
Directory structure
logger.py
parser/
__init__.py
docparser.py
sequence/
__init__.py
processor.py
tests/
testdocparser.py
/parser/docparser.py
import logger
from sequence.processor import Processor
class DocParser(object):
def __init__(self, reader_writer):
self.processor = Processor(reader_writer)
def write_and_parse(self, products):
logger.log(products)
self.processor.process(products)
/tests/testdocparser.py
import unittest
from mock import MagicMock, patch
from parser import docparser
class DocParserTests(unittest.TestCase):
def setUp(self):
self.mock_writer = MagicMock()
self.docparser = docparser.DocParser(self.mock_writer)
#patch("parser.docparser.logger") # This seems to be patched properly
#patch("parser.docparser.Processor") # This is not patched
def test_write_and_parse(self, mock_logger, mock_proc):
products = "products"
self.docparser.write_and_parse(products)
You patch Processor in test_write_and_parse() but it's instantiated in DocParser.__init__() which is called from setUp().
This should work, though I haven't tested it:
class DocParserTests(unittest.TestCase):
def setUp(self):
self.mock_writer = MagicMock()
with patch('parser.docparser.Processor'):
self.docparser = docparser.DocParser(self.mock_writer)
#patch("parser.docparser.logger")
def test_write_and_parse(self, mock_logger):
products = "products"
self.docparser.write_and_parse(products)
I've used context manager instead of decorator to avoid changing setUp() signature (adding an argument).
Also the order of mock arguments for test_write_and_parse() is incorrect in your code. Here's an excerpt from mock docs:
When you nest patch decorators the mocks are passed in to the decorated function in the same order they applied (the normal python order that decorators are applied). This means from the bottom up...
Correct order:
#patch("parser.docparser.logger") # This seems to be patched properly
#patch("parser.docparser.Processor") # This is not patched
def test_write_and_parse(self, mock_proc, mock_logger):
# ...
Of cource, it doesn't really matter in your particular case because mock_proc and mock_logger are not used later.
I'm mocking my RpcClient class for all of my unit tests like this:
import unittest2
from mock import patch
#patch('listeners.RpcClient')
class SomeTestCase(unittest2.TestCase):
test_something(self, mock_api):
...
test_something_else(self, mock_api):
...
For most of my tests I don't want to do any assertions using the mock object, all I want to do is patch the class so the RpcClient doesn't attempt to connect and fire requests for each of my tests (I have it hooked up to a post save event on one of my models).
Can I avoid passing in mock_api into every single one of my tests?
I ended up doing the mocking in setUp using patcher.start():
def setUp(self):
self.rpc_patcher = patch('listeners.RpcClient')
self.MockClass = rpc_patcher.start()
def tearDown(self):
self.rpc_patcher.stop()
So I don't have to decorate any of my test cases and don't have to add any extra arguments to my tests.
More info:
http://docs.python.org/dev/library/unittest.mock#patch-methods-start-and-stop
Can you just set a default parameter to mock_api?
def test_something(self, mock_api=None):
...
def test_something_else(self, mock_api=None):
You could make use mock.patch as a context manager for when you call the SUT. Something like:
import unittest2
from mock import patch
class SomeTestCase(unittest2.TestCase):
def call_sut(self, *args, **kwargs):
with mock.patch('listeners.RpcClient'):
# call SUT
def test_something(self):
self.call_sut()
# make assertions
def test_something_else(self):
self.call_sut()
# make assertions