26

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'.

2 Answers 2

31

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) 
Sign up to request clarification or add additional context in comments.

4 Comments

Ok, great that works for the loading workbook, but what if I want to mock the same method but with different arguments and have it return different values? For example, if I mock config = configparser.ConfigParser() and then I want to mock config.get('Section1','Value1') = MagicMock(return_value='val1') and config.get('Section1','Value2') = MagicMock(return_value='val2')?
You can access what arguments were passed with .call_args, and update what is returned with .return_value. docs.python.org/3/library/… docs.python.org/3/library/…
Look up side_effect for a MagicMock object. You set it to a function, vs return_value which is set to an explicit value. You can define the function with *args as argument and check it in the function body and return based on the args values.
What if production class is created 2 layers below test_xxx()
8

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() 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.