问题
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'
.
回答1:
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)
回答2:
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()
来源:https://stackoverflow.com/questions/43941015/mocking-method-calls-in-python