Mocking Method Calls In Python

风格不统一 提交于 2019-12-22 05:09:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!