Import only functions from a python file - python

I have many Python files (submission1.py, submission2.py, ... , submissionN.py) in the following format,
#submission1.py
def fun():
print('some fancy function')
fun()
I want to write a tester to test these submissions. (They are actually homeworks that I am grading.). I have a tester for the fun() which is able to test the function itself. However, my problem is, when I import submission.py, it runs the fun() since it calls it at the end of file.
I know that, using if __name__ == "__main__": is the correct way of handling this issue, however, our submissions does not have it since we did not teach it.
So, my question is, is there any way that I can import only fun() from the submission.py files without running the rest of the python file?

For simple scripts with just functions the following will work:
submission1.py:
def fun(x):
print(x)
fun("foo")
def fun2(x):
print(x)
fun2("bar")
print("debug print")
You can remove all bar the FunctionDef nodes then recompile:
import ast
import types
with open("submission1.py") as f:
p = ast.parse(f.read())
for node in p.body[:]:
if not isinstance(node, ast.FunctionDef):
p.body.remove(node)
module = types.ModuleType("mod")
code = compile(p, "mod.py", 'exec')
sys.modules["mod"] = module
exec(code, module.__dict__)
import mod
mod.fun("calling fun")
mod.fun2("calling fun2")
Output:
calling fun
calling fun2
The module body contains two Expr and one Print node which we remove in the loop keeping just the FunctionDef's.
[<_ast.FunctionDef object at 0x7fa33357f610>, <_ast.Expr object at 0x7fa330298a90>,
<_ast.FunctionDef object at 0x7fa330298b90>, <_ast.Expr object at 0x7fa330298cd0>,
<_ast.Print object at 0x7fa330298dd0>]
So after the loop out body only contains the functions:
[<_ast.FunctionDef object at 0x7f49a786a610>, <_ast.FunctionDef object at 0x7f49a4583b90>]
This will also catch where the functions are called with print which if the student was calling the function from an IDE where the functions have return statements is pretty likely, also to keep any imports of there are any you can keep ast.Import's and ast.ImportFrom's:
submission.py:
from math import *
import datetime
def fun(x):
print(x)
fun("foo")
def fun2(x):
return x
def get_date():
print(pi)
return datetime.datetime.now()
fun2("bar")
print("debug print")
print(fun2("hello world"))
print(get_date())
Compile then import:
for node in p.body[:]:
if not isinstance(node, (ast.FunctionDef,ast.Import, ast.ImportFrom)):
p.body.remove(node)
.....
import mod
mod.fun("calling fun")
print(mod.fun2("calling fun2"))
print(mod.get_date())
Output:
calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329
Lastly if you have some variables declared that you need to use you can keep them using ast.Assign:
submission.py:
from math import *
import datetime
AREA = 25
WIDTH = 35
def fun(x):
print(x)
fun("foo")
def fun2(x):
return x
def get_date():
print(pi)
return datetime.datetime.now()
fun2("bar")
print("debug print")
print(fun2("hello world"))
print(get_date()
Add ast.Assign:
for node in p.body[:]:
if not isinstance(node, (ast.FunctionDef,
ast.Import, ast.ImportFrom,ast.Assign)):
p.body.remove(node)
....
Output:
calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35
So it really all depends on how your modules are structured and what they should contain as to what you remove. If there are literally only functions then the first example will do what you want. If there are other parts that need to be kept it is just a matter of adding them to the isinstance check.
The listing of all the abstract grammar definitions is in the cpython source under Parser/Python.asdl.

You could use sys.settrace() to catch function definitions.
Whenever your fun() is defined, you save it somewhere, and you place a stub into the module you are importing, so that it won't get executed.
Assuming that fun() gets defined only once, this code should do the trick:
import sys
fun = None
def stub(*args, **kwargs):
pass
def wait_for_fun(frame, event, arg):
global fun
if frame.f_code.co_filename == '/path/to/module.py':
if 'fun' in frame.f_globals:
# The function has just been defined. Save it.
fun = frame.f_globals['fun']
# And replace it with our stub.
frame.f_globals['fun'] = stub
# Stop tracing the module execution.
return None
return wait_for_fun
sys.settrace(wait_for_fun)
import my_module
# Now fun() is available and we can test it.
fun(1, 2, 3)
# We can also put it again inside the module.
# This is important if other functions in the module need it.
my_module.fun = fun
This code can be improved in many ways, but it does its job.

maybe if you just want to import the fun () function from submission.py try
from submission import fun
To perform the function of fun, you must include the fun module
submission.fun()
or if you want to make it easier when calling the fun () function, give it a try
from submission import fun as FUN
FUN ()

Related

Pytest asserting a function with a return value that is coming from a user input [duplicate]

I have a console program written in Python. It asks the user questions using the command:
some_input = input('Answer the question:', ...)
How would I test a function containing a call to input using pytest?
I wouldn't want to force a tester to input text many many times only to finish one test run.
As The Compiler suggested, pytest has a new monkeypatch fixture for this. A monkeypatch object can alter an attribute in a class or a value in a dictionary, and then restore its original value at the end of the test.
In this case, the built-in input function is a value of python's __builtins__ dictionary, so we can alter it like so:
def test_something_that_involves_user_input(monkeypatch):
# monkeypatch the "input" function, so that it returns "Mark".
# This simulates the user entering "Mark" in the terminal:
monkeypatch.setattr('builtins.input', lambda _: "Mark")
# go about using input() like you normally would:
i = input("What is your name?")
assert i == "Mark"
You should probably mock the built-in input function, you can use the teardown functionality provided by pytest to revert back to the original input function after each test.
import module # The module which contains the call to input
class TestClass:
def test_function_1(self):
# Override the Python built-in input method
module.input = lambda: 'some_input'
# Call the function you would like to test (which uses input)
output = module.function()
assert output == 'expected_output'
def test_function_2(self):
module.input = lambda: 'some_other_input'
output = module.function()
assert output == 'another_expected_output'
def teardown_method(self, method):
# This method is being called after each test case, and it will revert input back to original function
module.input = input
A more elegant solution would be to use the mock module together with a with statement. This way you don't need to use teardown and the patched method will only live within the with scope.
import mock
import module
def test_function():
with mock.patch.object(__builtins__, 'input', lambda: 'some_input'):
assert module.function() == 'expected_output'
You can replace sys.stdin with some custom Text IO, like input from a file or an in-memory StringIO buffer:
import sys
class Test:
def test_function(self):
sys.stdin = open("preprogrammed_inputs.txt")
module.call_function()
def setup_method(self):
self.orig_stdin = sys.stdin
def teardown_method(self):
sys.stdin = self.orig_stdin
this is more robust than only patching input(), as that won't be sufficient if the module uses any other methods of consuming text from stdin.
This can also be done quite elegantly with a custom context manager
import sys
from contextlib import contextmanager
#contextmanager
def replace_stdin(target):
orig = sys.stdin
sys.stdin = target
yield
sys.stdin = orig
And then just use it like this for example:
with replace_stdin(StringIO("some preprogrammed input")):
module.call_function()
This can be done with mock.patch and with blocks in python3.
import pytest
import mock
import builtins
"""
The function to test (would usually be loaded
from a module outside this file).
"""
def user_prompt():
ans = input('Enter a number: ')
try:
float(ans)
except:
import sys
sys.exit('NaN')
return 'Your number is {}'.format(ans)
"""
This test will mock input of '19'
"""
def test_user_prompt_ok():
with mock.patch.object(builtins, 'input', lambda _: '19'):
assert user_prompt() == 'Your number is 19'
The line to note is mock.patch.object(builtins, 'input', lambda _: '19'):, which overrides the input with the lambda function. Our lambda function takes in a throw-away variable _ because input takes in an argument.
Here's how you could test the fail case, where user_input calls sys.exit. The trick here is to get pytest to look for that exception with pytest.raises(SystemExit).
"""
This test will mock input of 'nineteen'
"""
def test_user_prompt_exit():
with mock.patch.object(builtins, 'input', lambda _: 'nineteen'):
with pytest.raises(SystemExit):
user_prompt()
You should be able to get this test running by copy and pasting the above code into a file tests/test_.py and running pytest from the parent dir.
Since I need the input() call to pause and check my hardware status LEDs, I had to deal with the situation without mocking. I used the -s flag.
python -m pytest -s test_LEDs.py
The -s flag essentially means: shortcut for --capture=no.
You can do it with mock.patch as follows.
First, in your code, create a dummy function for the calls to input:
def __get_input(text):
return input(text)
In your test functions:
import my_module
from mock import patch
#patch('my_module.__get_input', return_value='y')
def test_what_happens_when_answering_yes(self, mock):
"""
Test what happens when user input is 'y'
"""
# whatever your test function does
For example if you have a loop checking that the only valid answers are in ['y', 'Y', 'n', 'N'] you can test that nothing happens when entering a different value instead.
In this case we assume a SystemExit is raised when answering 'N':
#patch('my_module.__get_input')
def test_invalid_answer_remains_in_loop(self, mock):
"""
Test nothing's broken when answer is not ['Y', 'y', 'N', 'n']
"""
with self.assertRaises(SystemExit):
mock.side_effect = ['k', 'l', 'yeah', 'N']
# call to our function asking for input
I don't have enough points to comment, but this answer: https://stackoverflow.com/a/55033710/10420225
doesn't work if you just copy/pasta.
Part One
For Python3, import mock doesn't work.
You need import unittest.mock and call it as unittest.mock.patch.object(), or from unittest import mock mock.patch.object()...
If using Python3.3+ the above should "just work". If using Python3.3- you need to pip install mock. See this answer for more info: https://stackoverflow.com/a/11501626/10420225
Part Two
Also, if you want to make this example more realistic, i.e. importing the function from outside the file and using it, there's more assembly required.
This is general directory structure we'll use
root/
src/prompt_user.py
tests/test_prompt_user.py
If function in external file
# /root/src/prompt_user.py
def user_prompt():
ans = input("Enter a number: ")
try:
float(ans)
except:
import sys
sys.exit("NaN")
return "Your number is {}".format(ans)
# /root/tests/test_prompt_user.py
import pytest
from unittest import mock
import builtins
from prompt_user import user_prompt
def test_user_prompt_ok():
with mock.patch.object(builtins, "input", lambda _: "19"):
assert user_prompt() == "Your number is 19"
If function in a class in external file
# /root/src/prompt_user.py
class Prompt:
def user_prompt(self):
ans = input("Enter a number: ")
try:
float(ans)
except:
import sys
sys.exit("NaN")
return "Your number is {}".format(ans)
# /root/tests/test_prompt_user.py
import pytest
from unittest import mock
import builtins
from mocking_test import Prompt
def test_user_prompt_ok():
with mock.patch.object(builtins, "input", lambda _: "19"):
assert Prompt.user_prompt(Prompt) == "Your number is 19"
Hopefully this helps people a bit more. I find these very simple examples almost useless because it leaves a lot out for real world use cases.
Edit: If you run into pytest import issues when running from external files, I would recommend looking over this answer: PATH issue with pytest 'ImportError: No module named YadaYadaYada'
A different alternative that does not require using a lambda function and provides more control during the tests is to use the mock decorator from the standard unittest module.
It also has the additional advantage of patching just where the object (i.e. input) is looked up, which is the recommended strategy.
# path/to/test/module.py
def my_func():
some_input = input('Answer the question:')
return some_input
# tests/my_tests.py
from unittest import mock
from path.to.test.module import my_func
#mock.patch("path.to.test.module.input")
def test_something_that_involves_user_input(mock_input):
mock_input.return_value = "This is my answer!"
assert my_func() == "This is my answer!"
mock_input.assert_called_once() # Optionally check one and only one call
The simplest way that works without mocking and easily in doctest for lightweight testing, is just making the input_function a parameter to your function and passing in this FakeInput class with the appropriate list of inputs that you want:
class FakeInput:
def __init__(self, input):
self.input = input
self.index = 0
def __call__(self):
line = self.input[self.index % len(self.input)]
self.index += 1
return line
Here is an example usage to test some functions using the input function:
import doctest
class FakeInput:
def __init__(self, input):
self.input = input
self.index = 0
def __call__(self):
line = self.input[self.index % len(self.input)]
self.index += 1
return line
def add_one_to_input(input_func=input):
"""
>>> add_one_to_input(FakeInput(['1']))
2
"""
return int(input_func()) + 1
def add_inputs(input_func=input):
"""
>>> add_inputs(FakeInput(['1', '5']))
6
"""
return int(input_func()) + int(input_func())
def return_ten_inputs(input_func=input):
"""
>>> return_ten_inputs(FakeInput(['1', '5', '7']))
[1, 5, 7, 1, 5, 7, 1, 5, 7, 1]
"""
return [int(input_func()) for _ in range(10)]
def print_4_inputs(input_func=input):
"""
>>> print_4_inputs(FakeInput(['1', '5', '7']))
1
5
7
1
"""
for i in range(4):
print(input_func())
if __name__ == '__main__':
doctest.testmod()
This also makes your functions more general so you can easily change them to take input from a file rather than the keyboard.
You can also use environment variables in your test code. For example if you want to give path as argument you can read env variable and set default value if it's missing.
import os
...
input = os.getenv('INPUT', default='inputDefault/')
Then start with default argument
pytest ./mytest.py
or with custom argument
INPUT=newInput/ pytest ./mytest.py

Pythons equivalent of #line in C [duplicate]

In C++, I can print debug output like this:
printf(
"FILE: %s, FUNC: %s, LINE: %d, LOG: %s\n",
__FILE__,
__FUNCTION__,
__LINE__,
logmessage
);
How can I do something similar in Python?
There is a module named inspect which provides these information.
Example usage:
import inspect
def PrintFrame():
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename) # __FILE__ -> Test.py
print(info.function) # __FUNCTION__ -> Main
print(info.lineno) # __LINE__ -> 13
def Main():
PrintFrame() # for this line
Main()
However, please remember that there is an easier way to obtain the name of the currently executing file:
print(__file__)
For example
import inspect
frame = inspect.currentframe()
# __FILE__
fileName = frame.f_code.co_filename
# __LINE__
fileNo = frame.f_lineno
There's more here http://docs.python.org/library/inspect.html
Building on geowar's answer:
class __LINE__(object):
import sys
def __repr__(self):
try:
raise Exception
except:
return str(sys.exc_info()[2].tb_frame.f_back.f_lineno)
__LINE__ = __LINE__()
If you normally want to use __LINE__ in e.g. print (or any other time an implicit str() or repr() is taken), the above will allow you to omit the ()s.
(Obvious extension to add a __call__ left as an exercise to the reader.)
You can refer my answer:
https://stackoverflow.com/a/45973480/1591700
import sys
print sys._getframe().f_lineno
You can also make lambda function
I was also interested in a __LINE__ command in python.
My starting point was https://stackoverflow.com/a/6811020 and I extended it with a metaclass object. With this modification it has the same behavior like in C++.
import inspect
class Meta(type):
def __repr__(self):
# Inspiration: https://stackoverflow.com/a/6811020
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
# print(info.filename) # __FILE__ -> Test.py
# print(info.function) # __FUNCTION__ -> Main
# print(info.lineno) # __LINE__ -> 13
return str(info.lineno)
class __LINE__(metaclass=Meta):
pass
print(__LINE__) # print for example 18
wow, 7 year old question :)
Anyway, taking Tugrul's answer, and writing it as a debug type method, it can look something like:
def debug(message):
import sys
import inspect
callerframerecord = inspect.stack()[1]
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename, 'func=%s' % info.function, 'line=%s:' % info.lineno, message)
def somefunc():
debug('inside some func')
debug('this')
debug('is a')
debug('test message')
somefunc()
Output:
/tmp/test2.py func=<module> line=12: this
/tmp/test2.py func=<module> line=13: is a
/tmp/test2.py func=<module> line=14: test message
/tmp/test2.py func=somefunc line=10: inside some func
import inspect
.
.
.
def __LINE__():
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back.f_lineno
def __FILE__():
return inspect.currentframe().f_code.co_filename
.
.
.
print "file: '%s', line: %d" % (__FILE__(), __LINE__())
Here is a tool to answer this old yet new question!
I recommend using icecream!
Do you ever use print() or log() to debug your code? Of course, you
do. IceCream, or ic for short, makes print debugging a little sweeter.
ic() is like print(), but better:
It prints both expressions/variable names and their values.
It's 40% faster to type.
Data structures are pretty printed.
Output is syntax highlighted.
It optionally includes program context: filename, line number, and parent function.
For example, I created a module icecream_test.py, and put the following code inside it.
from icecream import ic
ic.configureOutput(includeContext=True)
def foo(i):
return i + 333
ic(foo(123))
Prints
ic| icecream_test.py:6 in <module>- foo(123): 456
To get the line number in Python without importing the whole sys module...
First import the _getframe submodule:
from sys import _getframe
Then call the _getframe function and use its' f_lineno property whenever you want to know the line number:
print(_getframe().f_lineno) # prints the line number
From the interpreter:
>>> from sys import _getframe
... _getframe().f_lineno # 2
Word of caution from the official Python Docs:
CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.
In other words: Only use this code for personal testing / debugging reasons.
See the Official Python Documentation on sys._getframe for more information on the sys module, and the _getframe() function / submodule.
Based on Mohammad Shahid's answer (above).

Monkey patch a function in a module for unit testing

I have the following method in a module that calls another method imported from another module:
def imported_function():
do_unnecessary_things_for_unittest()
The actual method that needs to be tested, imports and uses the above function:
from somewhere import imported_function
def function_to_be_tested():
imported_function()
do_something_more()
return 42
The inner calls and related calculations inside imported_function are not important and they are not what I want to test so I just want to skip them while testing the actual function function_to_be_tested.
Thus, I tried to monkey patch the module named somewhere inside the test method but no luck.
def test_function_to_be_tested(self):
import somewhere
somewhere.__dict__['imported_function'] = lambda : True
The question is, how can I monkey patch a method of a module while testing so that it will not be called during the test phase?
I think better to use Mock Library
So you can do something like:
from somewhere import imported_function
#patch(imported_function)
def test_function_to_be_tested(self, imported_function):
imported_function.return_value = True
#Your test
I think for unit tests it's better than monkey patch.
Say you have the following files:
somewhere.py
def imported_function():
return False
testme.py
from somewhere import imported_function
def function_to_be_tested():
return imported_function()
A call to testme.function_to_be_tested() would return False.
Now, the trick is to import somewhere before testme:
import somewhere
somewhere.__dict__['imported_function'] = lambda : True
import testme
def test_function_to_be_tested():
print testme.function_to_be_tested()
test_function_to_be_tested()
Output:
True
Or, reload the testme module
import testme
def test_function_to_be_tested():
print testme.function_to_be_tested()
import somewhere
somewhere.__dict__['imported_function'] = lambda : True
print testme.function_to_be_tested()
reload(testme)
print testme.function_to_be_tested()
test_function_to_be_tested()
Output:
False
False
True

How to determine file, function and line number?

In C++, I can print debug output like this:
printf(
"FILE: %s, FUNC: %s, LINE: %d, LOG: %s\n",
__FILE__,
__FUNCTION__,
__LINE__,
logmessage
);
How can I do something similar in Python?
There is a module named inspect which provides these information.
Example usage:
import inspect
def PrintFrame():
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename) # __FILE__ -> Test.py
print(info.function) # __FUNCTION__ -> Main
print(info.lineno) # __LINE__ -> 13
def Main():
PrintFrame() # for this line
Main()
However, please remember that there is an easier way to obtain the name of the currently executing file:
print(__file__)
For example
import inspect
frame = inspect.currentframe()
# __FILE__
fileName = frame.f_code.co_filename
# __LINE__
fileNo = frame.f_lineno
There's more here http://docs.python.org/library/inspect.html
Building on geowar's answer:
class __LINE__(object):
import sys
def __repr__(self):
try:
raise Exception
except:
return str(sys.exc_info()[2].tb_frame.f_back.f_lineno)
__LINE__ = __LINE__()
If you normally want to use __LINE__ in e.g. print (or any other time an implicit str() or repr() is taken), the above will allow you to omit the ()s.
(Obvious extension to add a __call__ left as an exercise to the reader.)
You can refer my answer:
https://stackoverflow.com/a/45973480/1591700
import sys
print sys._getframe().f_lineno
You can also make lambda function
I was also interested in a __LINE__ command in python.
My starting point was https://stackoverflow.com/a/6811020 and I extended it with a metaclass object. With this modification it has the same behavior like in C++.
import inspect
class Meta(type):
def __repr__(self):
# Inspiration: https://stackoverflow.com/a/6811020
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
# print(info.filename) # __FILE__ -> Test.py
# print(info.function) # __FUNCTION__ -> Main
# print(info.lineno) # __LINE__ -> 13
return str(info.lineno)
class __LINE__(metaclass=Meta):
pass
print(__LINE__) # print for example 18
wow, 7 year old question :)
Anyway, taking Tugrul's answer, and writing it as a debug type method, it can look something like:
def debug(message):
import sys
import inspect
callerframerecord = inspect.stack()[1]
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename, 'func=%s' % info.function, 'line=%s:' % info.lineno, message)
def somefunc():
debug('inside some func')
debug('this')
debug('is a')
debug('test message')
somefunc()
Output:
/tmp/test2.py func=<module> line=12: this
/tmp/test2.py func=<module> line=13: is a
/tmp/test2.py func=<module> line=14: test message
/tmp/test2.py func=somefunc line=10: inside some func
import inspect
.
.
.
def __LINE__():
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back.f_lineno
def __FILE__():
return inspect.currentframe().f_code.co_filename
.
.
.
print "file: '%s', line: %d" % (__FILE__(), __LINE__())
Here is a tool to answer this old yet new question!
I recommend using icecream!
Do you ever use print() or log() to debug your code? Of course, you
do. IceCream, or ic for short, makes print debugging a little sweeter.
ic() is like print(), but better:
It prints both expressions/variable names and their values.
It's 40% faster to type.
Data structures are pretty printed.
Output is syntax highlighted.
It optionally includes program context: filename, line number, and parent function.
For example, I created a module icecream_test.py, and put the following code inside it.
from icecream import ic
ic.configureOutput(includeContext=True)
def foo(i):
return i + 333
ic(foo(123))
Prints
ic| icecream_test.py:6 in <module>- foo(123): 456
To get the line number in Python without importing the whole sys module...
First import the _getframe submodule:
from sys import _getframe
Then call the _getframe function and use its' f_lineno property whenever you want to know the line number:
print(_getframe().f_lineno) # prints the line number
From the interpreter:
>>> from sys import _getframe
... _getframe().f_lineno # 2
Word of caution from the official Python Docs:
CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.
In other words: Only use this code for personal testing / debugging reasons.
See the Official Python Documentation on sys._getframe for more information on the sys module, and the _getframe() function / submodule.
Based on Mohammad Shahid's answer (above).

Mocking Functions Using Python Mock

I am trying to Mock a function (that returns some external content) using the python mock module.
I'm having some trouble mocking functions that are imported into a module.
For example, in util.py I have
def get_content():
return "stuff"
I want to mock util.get_content so that it returns something else.
I am trying this:
util.get_content=Mock(return_value="mocked stuff")
If get_content gets invoked inside another module, it never actually seems to return the mocked object. Am I missing something in terms of how to use Mock?
Note that if I invoke the following, things work correctly:
>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"
However, if get_content is called from inside another module, it invokes the original function instead of the mocked version:
>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"
Contents of mymodule.py
from util import get_content
class MyObj:
def func():
get_content()
So I guess my question is - how do I get invoke the Mocked version of a function from inside a module that I call?
It appears that the from module import function may be to blame here, in that it doesn't point to the Mocked function.
The general case would be to use patch from mock. Consider the following:
utils.py
def get_content():
return 'stuff'
mymodule.py
from util import get_content
class MyClass(object):
def func(self):
return get_content()
test.py
import unittest
from mock import patch
from mymodule import MyClass
class Test(unittest.TestCase):
#patch('mymodule.get_content')
def test_func(self, get_content_mock):
get_content_mock.return_value = 'mocked stuff'
my_class = MyClass()
self.assertEqual(my_class.func(), 'mocked stuff')
self.assertEqual(get_content_mock.call_count, 1)
get_content_mock.assert_called_once()
Note how get_content is mocked, it is not util.get_content, rather mymodule.get_content since we are using it in mymodule.
Above has been tested with mock v2.0.0, nosetests v1.3.7 and python v2.7.9.
I think I have a workaround, though it's still not quite clear on how to solve the general case
In mymodule, if I replace
from util import get_content
class MyObj:
def func():
get_content()
with
import util
class MyObj:
def func():
util.get_content()
The Mock seems to get invoked. It looks like the namespaces need to match (which makes sense). However, the weird thing is that I would expect
import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")
to do the trick in the original case where I am using the from/import syntax (which now pulls in get_content into mymodule). But this still refers to the unmocked get_content.
Turns out the namespace matters - just need to keep that in mind when writing your code.
You have to patch the function where it is being used. In your case that would be in the mymodule module.
import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"
There is a reference in the docs here: http://docs.python.org/dev/library/unittest.mock.html#where-to-patch
Let's assume you're creating your mock inside module foobar:
import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")
If you import mymodule and call util.get_content without first importing foobar, your mock will not be installed:
import util
def func()
print util.get_content()
func()
"stuff"
Instead:
import util
import foobar # substitutes the mock
def func():
print util.get_content()
func()
"mocked stuff"
Note that foobar can be imported from anywhere (module A imports B which imports foobar) as long as foobar is evaluated before util.get_content is called.
While it doesn't provide an answer to your question directly, another possible alternative is to transform your function to a static method using the #staticmethod.
So you could transform your module utils into a class using something like:
class util(object):
#staticmethod
def get_content():
return "stuff"
Then mock patches it correctly.

Categories