How can I mock out this python function properly? - python

I have the following function in a file called my_file.py:
import logging
log = logging.getLogger(__name__)
class MyClass:
def my_func(self):
print("Entered my_func()")
print("log.error = {}".format(log.error))
log.error("Hello World")
When my_func() is run, I see this output:
Entered my_func()
log.error = <bound method ProcessAwareLogger.error of <celery.utils.log.ProcessAwareLogger object at 0x7fd52d4f0d50>>
I want to write a unit-test case to check that whenever my_func() is called, log.error() is called with the argument "Hello World".
So my test case looks like this:
import mock
from my_file import MyClass
#mock.patch("my_file.logging.error")
def test_parse_response_with_error(self, mock_log_error):
m = MyClass()
m.my_func()
mock_log_error.assert_called_once_with("Hello World")
When I run this test case, I get the error: AssertionError: Expected to be called once. Called 0 times. So my patch apparently didn't work. Did I specify the path my_file.logging.error wrong? How can I make this work properly so that the unit-test case passes?

You mocked the wrong thing. log.error creates a bound method that is distinct from logging.error, and that method already has a reference to the "real" logging.error saved before your patch was created.
Instead, mock the error attribute of the Logger instance directly.
#mock.patch.object(my_file.log, 'error'):
def test_it(self, mock_log_error):
m = MyClass()
m.my_func()
mock_log_error.assert_called_once_with("Hello World")
(It's possible that patching logging.error before importing my_file might work.)

Related

Python: Getting mocked function value returned, not MagicMock, without adding returnvalue

So, basically I want to mock a function imported in another class, and for some reason I can't retrieve the mocked result without calling returnvalue of the mock.
This is the setup: one file, one module, one test class. I want to mock functionB() in source.fileB.
source.fileB
def functionB():
print('toto')
source.fileA
from source.fileB import *
def functionA():
print("bar")
return functionB()
Test case
from source.fileA import functionA
from source.fileA import functionB
#mock.patch('source.fileA.functionB')
def test_functionA(functionB_mock):
functionB_mock().returnvalue = "foo"
print(functionB_mock) # prints MagicMock object named functionB
print(functionB_mock.returnvalue) # prints MagicMock object name functionB.returnvalue
print(functionB_mock().returnvalue) #prints "foo"
print(functionA().returnvalue) #executes functionA, prints both "bar" and "foo"
print(functionA()) #executes functionA, prints both "bar" and MagicMock object named functionB()
So every time I try to get the result of the mocked functionB(), I have to use returnvalue. This is driving me nuts as I cannot update functionA() with
return functionB().returnvalue
in order for the rest of the code under test to execute properly.
I must be doing something wrong, but I can't understand what precisely.
There are two problems:
The mock attribute you want is return_value, not returnvalue!
You need to set that attribute on the mock itself, not the result of calling the mock (which is a different mock).
Here's a self-contained (single-file) version of your test code with the fix and some explanatory comments.
import mock
def functionB():
print('toto')
def functionA():
print("bar")
return functionB()
#mock.patch('__main__.functionB')
def test_functionA(functionB_mock):
functionB_mock.return_value = "foo"
# Prints a mock object because that's what functionB_mock is.
print(functionB_mock)
# Prints "foo" because that's what functionB_mock returns.
print(functionB_mock())
# The following two lines would raise AttributeError because
# "foo" isn't a Mock and doesn't have a 'return_value' attribute!
# print(functionB_mock().return_value)
# print(functionA().return_value)
# Executes functionA, prints "bar" and "foo"
print(functionA())
test_functionA()

Python unittest: Unable to mock imported functions so that conditional evaluates to False

I'm encountering a problem with unit testing in Python. Specifically, when I try to mock a function my code imports, variables assigned to the output of that function get assigned to a MagicMock object instead of the mock-function's return_value. I've been digging through the docs for python's unittest library, but am not having any luck.
The following is the code I want to test:
from production_class import function_A, function_B, function_M
class MyClass:
def do_something(self):
variable = functionB()
if variable:
do_other_stuff()
else:
do_something_else
this is what I've tried:
#mock.patch(path.to.MyClass.functionB)
#mock.patch(<other dependencies in MyClass>)
def test_do_something(self, functionB_mock):
functionB_mock.return_value = None # or False, or 'foo' or whatever.
myClass = MyClass()
myClass.do_something()
self.assertTrue(else_block_was_executed)
The issue I have is that when the test gets to variable = functionB in MyClass, the variable doesn't get set to my return value; it gets set to a MagicMock object (and so the if-statement always evaluates to True). How do I mock an imported function such that when executed, variables actually get set to the return value and not the MagicMock object itself?
We'd have to see what import path you're actually using with path.to.MyClass.functionB. When mocking objects, you don't necessarily use the path directly to where the object is located, but the one that the intepreter sees when recursively importing modules.
For example, if your test imports MyClass from myclass.py, and that file imports functionB from production_class.py, the mock path would be myclass.functionB, instead of production_class.functionB.
Then there's the issue that you need additional mocks of MyClass.do_other_stuff and MyClass.do_something_else in to check whether MyClass called the correct downstream method, based on the return value of functionB.
Here's a working example that tests both possible return values of functionB, and whether they call the correct downstream method:
myclass.py
from production_class import functionA, functionB, functionM
class MyClass:
def do_something(self):
variable = functionB()
if variable:
self.do_other_stuff()
else:
self.do_something_else()
def do_other_stuff(self):
pass
def do_something_else(self):
pass
production_class.py
import random
def functionA():
pass
def functionB():
return random.choice([True, False])
def functionM():
pass
test_myclass.py
import unittest
from unittest.mock import patch
from myclass import MyClass
class MyTest(unittest.TestCase):
#patch('myclass.functionB')
#patch('myclass.MyClass.do_something_else')
def test_do_something_calls_do_something_else(self, do_something_else_mock, functionB_mock):
functionB_mock.return_value = False
instance = MyClass()
instance.do_something()
do_something_else_mock.assert_called()
#patch('myclass.functionB')
#patch('myclass.MyClass.do_other_stuff')
def test_do_something_calls_do_other_stuff(self, do_other_stuff_mock, functionB_mock):
functionB_mock.return_value = True
instance = MyClass()
instance.do_something()
do_other_stuff_mock.assert_called()
if __name__ == '__main__':
unittest.main()
calling python test_myclass.py results in:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
What I wound up doing was changing the import statements in MyClass to import the object instead of the individual methods. I was then able to mock the object without any trouble.
More explicitly I changed MyClass to look like this:
import production_class as production_class
class MyClass:
def do_something(self):
variable = production_class.functionB()
if variable:
do_other_stuff()
else:
do_something_else
and changed my test to
#mock.patch(path.to.MyClass.production_class)
def test_do_something(self, prod_class_mock):
prod_class_mock.functionB.return_value = None
myClass = MyClass()
myClass.do_something()
self.assertTrue(else_block_was_executed)

Test instance of class that mocked method is called from in Python

I am mocking out a method of a class and want to test the instance of the class that the method was called from to test that the creation part of my function works as expected.
In my particular case do_stuff tries to write bar_instance to an Excel File and I don't want that to happen i.e.
def create_instance(*args):
return Bar(*args)
class Bar():
def __init__(self, *args):
self.args = args
def do_stuff(self):
pass
def foo(*args):
bar_instance = create_instance(*args)
bar_instance.do_stuff()
Then in a testing file
from unittest import TestCase
from unittest.mock import patch
from path.to.file import foo
class TestFoo(TestCase):
#patch('path.to.file.Bar.do_stuff')
def test_foo(self, mock_do_stuff):
test_args = [1]
_ = foo(*test_args)
# Test here the instance of `Bar` that `mock_do_stuff` was called from
# Something like
actual_args = list(bar_instance.args)
self.assertEqual(test_args, actual_args)
I put a break in the test function after foo(*test_args) is run but can't see any way from the mocked method of accessing the instance of Bar it was called from and am a bit stuck. I don't want to mock out Bar further up the code as I want to make sure the correct instance of Bar is being created.
In your code example, there are three things that might need testing: function create_instance, class Bar and function foo. I understand your test code such that you want to ensure that function foo calls do_stuff on the instance returned by create_instance.
Since the original create_instance function has control over the created instance, a solution of your problem is to mock create_instance such that your test gains control of the object that is handed over to foo:
import unittest
from unittest import TestCase
from unittest.mock import patch, MagicMock
from SO_60624698 import foo
class TestFoo(TestCase):
#patch('SO_60624698.create_instance')
def test_foo_calls_do_stuff_on_proper_instance (
self, create_instance_mock ):
# Setup
Bar_mock = MagicMock()
create_instance_mock.return_value = Bar_mock
# Exercise
foo(1, 2, 3) # args are irrelevant
# Verify
Bar_mock.do_stuff.assert_called()
if __name__ == '__main__':
unittest.main()
In addition, you might also want to test if foo passes the arguments correctly to create_instance. This could be implemented as a separate test:
...
#patch('SO_60624698.create_instance')
def test_foo_passes_arguments_to_create_instance (
self, create_instance_mock ):
# Setup
create_instance_mock.return_value = MagicMock()
# Exercise
foo(1, 22, 333)
# Verify
create_instance_mock.assert_called_with(1, 22, 333)
And, certainly, to complete the whole test of the object generation, you could test create_instance directly, by calling it and checking on the returned instance of Bar if it has used its arguments correctly for the construction of the Bar instance.
As patch returns an instance of Mock (or actually MagicMock, but it inherits the relevant methods from its base - Mock), you have the assert_called_with method available, which should do the trick.
Note that this method is sensitive to args/kwargs - you have to assert the exact same call.
Another note: it might be a better practice to use patch.object instead of patch here

Extend class with specific methods from another module

I am trying to extend my custom class 'Test' with a few of the output functions contained within the 'logging' module. I have a custom logging level called 'print' which is causing me issues. I have tried multiple methods and each has some drawback. Is there any other methods that would be better that I am missing.
I want to be able to use vars() on the object while and only print out the attributes that aren't logging methods.
This method works but prints out the 'print' method (which I don't want)
#Test.py
logger = logging.getLogger('console')
class Test:
def __init__(self):
self._some_obj = True
self.print= logger.print
#main.py
vars(Test()) # Prints {'_some_obj':True,'print': <bound method Logger.printof <Logger console (PRINT)>>}
This method correctly only prints out only correct attributes but overwrites default print in the process
#Test.py
logger = logging.getLogger('console')
class Test:
print = logger.print # Overwrites default print
def __init__(self):
self._some_obj = True
#main.py
vars(Test()) # Prints {'_some_obj':True}
This method also works but doesn't seem very pythonic. Since I will be doing this to alot of functions it will take up alot of space and I have also had issues passing through optional parameters
#Test.py
logger = logging.getLogger('console')
class Test:
def __init__(self):
self._some_obj = True
def print(msg):
logger.print(msg)
#main.py
vars(Test()) # Prints {'_some_obj':True}

Python's mock.patch not patching class in unittest

I'm getting confused by using Mock in my python unittests. I've made this simplified version of my problem:
I have this dummy class and methods:
# app/project.py
class MyClass(object):
def method_a(self):
print(FetcherA)
results = FetcherA()
Which is using this class:
# app/fetch.py
class FetcherA(object):
pass
And then this test:
# app/tests/test.py
from mock import patch
from django.test import TestCase
from ..project import MyClass
class MyTestCase(TestCase):
#patch('app.fetch.FetcherA')
def test_method_a(self, test_class):
MyClass().method_a()
test_class.assert_called_once_with()
I would expect that running this test would pass and that print statement, for debugging, would output something like <MagicMock name=...>. Instead it prints out <class 'app.fetch.FetcherA'> and I get:
AssertionError: Expected to be called once. Called 0 times.
Why isn't FetcherA being patched?
OK, fourth time through I think I understood the 'Where to patch' section of the Mock docs.
So, instead of:
#patch('app.fetch.FetcherA')
I should use:
#patch('app.project.FetcherA')
Because we're testing the code in app.project.MyClass where FetcherA has been imported already. So at that point FetcherA is availably globally(?) within app.project.

Categories

Resources