I have been searching stack exchange and around the web for how to do this, but I cannot understand how to mock behaviors for methods. I am trying to mock openpyxl behaviors and behaviors for my custom class. Here is my attempt:
import unittest
from unittest.mock import MagicMock
import openpyxl
from MyPythonFile import MyClass
class TestMyClass(unittest.TestCase):
def test_myclass(self):
myclass = MyClass()
wb = openpyxl.workbook()
ws = openpyxl.worksheet()
wbPath = 'wbPath'
openpyxl.load_workbook(wbPath, data_only = True) = MagicMock(return_value=wb)
When I try the final line I get the error "can't assign to function call". Do I need to use patch.object('openpyxl','load_workbook')
? I am used to mocking in Java with Groovy and it's pretty straightforward.
*Edit: wanted to add the finalized version of the test based on the response from @alxwrd
import unittest
from unittest.mock import MagicMock
import openpyxl
import configparser
from MyPythonFile import MyClass
class TestMyClass(unittest.TestCase):
def test_myclass(self):
myclass = MyClass()
wb = openpyxl.workbook()
ws = openpyxl.worksheet()
config = configparser.ConfigParser()
openpyxl.load_workbook = MagicMock(return_value=wb)
wb.get_sheet_by_name = MagicMock(return_value=ws)
config.sections() = MagicMock(return_value=['Section1'])
config.get = MagicMock(side_effect=['Value1','Value2'])
Notice that config.get gives multiple returns with the side_effect parameter, so if config.get()
is called once in the code it returns 'Value1'
and when config.get()
is called a second time it returns 'Value2'
.
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. unittest. mock provides a core Mock class removing the need to create a host of stubs throughout your test suite.
mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.
Mock vs. So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.
You can't override a function call, but you can override the function itself.
From the docs:
>>> from unittest.mock import MagicMock >>> thing = ProductionClass() >>> thing.method = MagicMock(return_value=3) >>> thing.method(3, 4, 5, key='value') 3 >>> thing.method.assert_called_with(3, 4, 5, key='value')
So in your case:
openpyxl.load_workbook = MagicMock(return_value=wb)
You don't have to import the target you want to mock in your unit tests. Use patch to mock the target. Let's assume your code has this import statement: import openpyxl
. Patch then can be used in your test as a decorator:
import unittest
from unittest import mock
from MyPythonFile import MyClass
class TestMyClass(unittest.TestCase):
@mock.patch('MyPythonFile.openpyxl')
def test_myclass(self, openpyxl_mock):
wb_dummy = 'foo'
openpyxl_mock.load_workbook.return_value = wb_dummy
myclass = MyClass()
myclass.load_workbook() # assuming this calls openpyxl.load_workbook()
Note that you have to add an argument to the test method which will get the mock object.
Or as a context manager:
import unittest
from unittest import mock
from MyPythonFile import MyClass
class TestMyClass(unittest.TestCase):
def test_myclass(self):
with mock.patch('MyPythonFile.openpyxl') as openpyxl_mock:
wb_dummy = 'foo'
openpyxl_mock.load_workbook.return_value = wb_dummy
myclass = MyClass()
myclass.load_workbook() # assuming this calls openpyxl.load_workbook()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With