Mock the input() in the module body - python-3.x

I've read a lot of materials, but nothing helped. I tried to write some unit tests using pytest and faced the following problem: i can mock the input() inside the function i want to test (using #mock.patch or monkeypatch.setattr for builtins.input).
This is the code in my module:
def double():
a = input()
return int(a) * 2
And this is my test file:
# #mock.patch('builtins.input', return_value='13')
def testing(monkeypatch):
monkeypatch.setattr('builtins.input', lambda: "13")
assert double() == 26
But when i have input() in the body of my module i get the error - OSError: pytest: reading from stdin while output is captured! Consider using -s.
Example:
def double(a):
return int(a) * 2
x = input()
I can fix this with adding if __name__ == "__main__" before x = input(), but i'm curious if i can solve this in any other way. Thank you for your help in advance!

Related

Is there a way in Python to simulate keyboard input to a function that asks for input?

I have a function in my code that asks the user for input:
def function_1():
...
x = input('Please provide input')
...
return something
I want to be able to run my code, and when the program eventually reaches function_1 and asks the user for input, automatically provide it with some specified input. When unittesting, I can use the mock library to simulate keyboard input as below
#mock.patch('builtins.input', side_effects=[1,2,3])
function_1()
function_1()
function_1()
This calls the function three times and provides the inputs {1, 2, 3}. I'm wondering if there is a way to do the same thing outside of unittesting.
I'm aware that I can rewrite the code, or use pipe in terminal. But I'm more curious about whether this can be solved in the manner described above.
One way is to overwrite sys.stdin:
import sys
from io import StringIO
oldstdin = sys.stdin
sys.stdin = StringIO("1\n2\n3\n")
assert input() == "1"
assert input() == "2"
assert input() == "3"
sys.stdin = oldstdin
The great thing about Python is that you can override just about any function, even built-ins.
def override():
from itertools import count
counter = count()
return lambda *args, **kwargs: next(counter)
input = override()
def x():
return input("Testing123")
print(x()) # 1
print(x()) # 2
print(x()) # 3
Though, this has to be done before your functions are called.

unit test in python for function that has no return value

I have a function that should pick only top 10 requests from list of requests:
def prioritize_top_10_requests(list_of_requests):
if not list_of_requests:
system.exit(1)
else:
for i in list_of_requests[0:10]:
print i
I want to write a unit test for this function just to check if 'i' is printed 10 times only though we have more list of requests.
I am writing python unit test cases for the first time.
I don't have python3 set up. The below code works with Python 3.
What it does is the following.
Created a UnitTest TestCase with one test, "test_priority".
In the test, redirect the stdout to a string.
Then execute the function.
Collect the print results from the string.
Remove None and empty strings and find the length.
Check if it is equal to 10.
import unittest
from io import StringIO
import sys
def prioritize_top_10_requests(list_of_requests):
for i in list_of_requests[0:10]:
print(i)
class TestMyCode(unittest.TestCase):
def test_priority(self):
sys.stdout = result = StringIO()
prioritize_top_10_requests(range(100))
sys.stdout = sys.__stdout__
printed_lines = result.getvalue()
print_count = len(printed_lines.strip().split('\n'))
self.assertEqual(print_count, 10)
if __name__ == '__main__':
unittest.main()

How can I redirect hardcoded calls to open to custom files?

I've written some python code that needs to read a config file at /etc/myapp/config.conf . I want to write a unit test for what happens if that file isn't there, or contains bad values, the usual stuff. Lets say it looks like this...
""" myapp.py
"""
def readconf()
""" Returns string of values read from file
"""
s = ''
with open('/etc/myapp/config.conf', 'r') as f:
s = f.read()
return s
And then I have other code that parses s for its values.
Can I, through some magic Python functionality, make any calls that readconf makes to open redirect to custom locations that I set as part of my test environment?
Example would be:
main.py
def _open_file(path):
with open(path, 'r') as f:
return f.read()
def foo():
return _open_file("/sys/conf")
test.py
from unittest.mock import patch
from main import foo
def test_when_file_not_found():
with patch('main._open_file') as mopen_file:
# Setup mock to raise the error u want
mopen_file.side_effect = FileNotFoundError()
# Run actual function
result = foo()
# Assert if result is expected
assert result == "Sorry, missing file"
Instead of hard-coding the config file, you can externalize it or parameterize it. There are 2 ways to do it:
Environment variables: Use a $CONFIG environment variable that contains the location of the config file. You can run the test with an environment variable that can be set using os.environ['CONFIG'].
CLI params: Initialize the module with commandline params. For tests, you can set sys.argv and let the config property be set by that.
In order to mock just calls to open in your function, while not replacing the call with a helper function, as in Nf4r's answer, you can use a custom patch context manager:
from contextlib import contextmanager
from types import CodeType
#contextmanager
def patch_call(func, call, replacement):
fn_code = func.__code__
try:
func.__code__ = CodeType(
fn_code.co_argcount,
fn_code.co_kwonlyargcount,
fn_code.co_nlocals,
fn_code.co_stacksize,
fn_code.co_flags,
fn_code.co_code,
fn_code.co_consts,
tuple(
replacement if call == name else name
for name in fn_code.co_names
),
fn_code.co_varnames,
fn_code.co_filename,
fn_code.co_name,
fn_code.co_firstlineno,
fn_code.co_lnotab,
fn_code.co_freevars,
fn_code.co_cellvars,
)
yield
finally:
func.__code__ = fn_code
Now you can patch your function:
def patched_open(*args):
raise FileNotFoundError
with patch_call(readconf, "open", "patched_open"):
...
You can use mock to patch a module's instance of the 'open' built-in to redirect to a custom function.
""" myapp.py
"""
def readconf():
s = ''
with open('./config.conf', 'r') as f:
s = f.read()
return s
""" test_myapp.py
"""
import unittest
from unittest import mock
import myapp
def my_open(path, mode):
return open('asdf', mode)
class TestSystem(unittest.TestCase):
#mock.patch('myapp.open', my_open)
def test_config_not_found(self):
try:
result = myapp.readconf()
assert(False)
except FileNotFoundError as e:
assert(True)
if __name__ == '__main__':
unittest.main()
You could also do it with a lambda like this, if you wanted to avoid declaring another function.
#mock.patch('myapp.open', lambda path, mode: open('asdf', mode))
def test_config_not_found(self):
...

Python: mock file input for testing function

I know there are similar posts, but I did not find anything like this one.
I have a function in python that receives as input the filename to be read and process and return something, and I want to test if the output for my function. Example:
#main function
def myfunction(filename):
f=open(filename)
for line in f:
# process data
pass
f.close()
return # something
#test function for the main function
def test_myfunction():
mockfile = #mymockfile
assert myfunction(mockfile) == #something
How could I create a mock file to test this function without having to write a read file?
This was the closest I found to emulate what i need (http://www.voidspace.org.uk/python/mock/helpers.html#mock-open)
Having struggled with the same question, please find my answers below.
update December 2022
I found a simpler solution than my original solution from 2018. Using Python 3.9.2 and running the script from the command line with pytest:
import unittest.mock
#main function
def myfunction(filename):
f=open(filename)
maximum = 0
for line in f:
if maximum < len(line):
maximum = len(line)
pass
f.close()
return maximum
def test_myfunction():
mock_file = unittest.mock.mock_open(read_data=('12characters\n13_characters'))
with unittest.mock.patch('builtins.open', mock_file):
actual_result = myfunction('foo')
assert not actual_result == 12
assert actual_result == 13
original answer 2018
I used Python 3.6 and Py.test through the pydev plugin in Eclipse.
import unittest.mock as mock
from unittest.mock import mock_open
#main function
def myfunction(filename):
f=open(filename)
maximum = 0
for line in f:
if maximum < len(line):
maximum = len(line)
pass
f.close()
return maximum
#test function for the main function
#mock.patch('builtins.open', new_callable=mock_open, create=True)
def test_myfunction(mock_open):
mock_open.return_value.__enter__ = mock_open
mock_open.return_value.__iter__ = mock.Mock(
return_value = iter(['12characters', '13_characters']))
answer = myfunction('foo')
assert not answer == 12
assert answer == 13

Testing a module with input() and print() inside - using TestCase

Situation:
A students task is to write a python script, doing something with only input() and print().
Example task: "Write python script that asks for number 'n' and prints string of 'n' stars."
He writes a script like this:
solution.py
n = int(input())
print('*' * n)
I want to write python module, that tests that his script is working correctly.
My solution till now is to do this:
test.py
from io import StringIO
import sys
sys.stdout = StringIO()
sys.stdin = StringIO(str(2))
import riesenie
s = sys.stdout.getvalue()
if s != '**' * 2 + '\n':
raise Exception('WRONG')
else:
print("OK", file=sys.stderr)
BUT, I would like to achieve this behaviour using TestCase. Something like:
test.py
from unittest import TestCase
class TestSolution(TestCase):
def test_stars(self):
sys.stdout = StringIO()
sys.stdin = StringIO(str(2))
import riesenie
s = sys.stdout.getvalue()
self.assertEqual('**', s)
But this is not working.
Is there a way to achieve this? Thank you in advance.

Resources