How to test a function with input() call (unittest) - python

I want to test the following module. There are hundreds of similar modules, and the test data for each module is stored on input_suffix.txt and output_suffix.txt (suffix is the name of the module).
def func(data):
# do something
return something
def reader():
for case_no in range(int(input())):
n_rows, n_cols = map(int, input().strip('\n').split())
data = [[int(n) for n in input().strip('\n').split()] for _ in range(n_rows)]
print(f'Case #{case_no + 1}: {func(data)}')
if __name__ == '__main__':
reader()
I could simply read both input_suffix.txt and output_suffix.txt files for the module to be tested, and process the input data to feed data right into the func() and get the job done by comparing the returned value to the expected output, which also could be processed from output data.
However, there are hundreds of similar modules, and they don't always share the same structure. The first line of input_suffix.txt always indicate the number of included tests, but after that, it varies all over. Sometimes, each test starts with a line that contains a number of rows(num_rows) and columns(num_cols) and the following num_rows lines is a representation of an array (just like the above). But input_suffix.txt may not have num_rows or an array representation at all, or may have multiple arrays, or other types of data. Likewise, arguments for the func() varies from module to module as well.
Because of the variations, I need to modify hundreds of test drivers to feed correctly pre-processed data to func() equivalents of each module. To avoid the tiresome task, these are what I need;
I need a way to pass the data (taken from input_suffix.txt) to the input() of each reader() on a line by line basis (each reader() of each module already has an adequate data processing scheme, so if I just pass the content of input_suffix.txt line by line, it will work).
I need a way to capture the printed data from reader() to compare it to the expected output taken from output_suffix.txt.
I have input_suffix.txt and output_suffix.txt on i and o, but I didn't know how to parse the printed value to compare it to o. That was the point where I was stuck, but after some trouble, I managed to get a working code. But I assume this is not the standard way for the job.
from contextlib import redirect_stdout
from io import StringIO
from unittest import TestCase
from unittest.mock import MagicMock, patch
from suffix import *
class Tester(TestCase):
def test(self):
with open('input_suffix.txt', 'r') as f:
i = [line.strip('\n') for line in f.readlines()]
with open('output_suffix.txt', 'r') as f:
o = [line.strip('\n') for line in f.readlines()]
m = MagicMock()
f = StringIO()
length = len(i)
with patch('builtins.input', side_effect=i):
reader()
for _ in range(length):
m()
result = f.getvalue().split('\n')[:-1]
for case_no, (actual, expected) in enumerate(zip(result, to)):
self.assertEqual(actual, expected)
print(f'Test {case_no + 1} Passed')
Could you suggest the proper way of doing this? Any help would be appreciated. Thanks.

You could mock the input using the patch that is available from the unittest library. The simple code to mock the input from the user is shown below.
Actual sample code: placed in path utils/sample.py
def take_input():
response = input("please type a:")
if response == 'a':
return "Correct"
return "Not Correct"
Actual test code:
from utils.sample import take_input
import unittest
from unittest.mock import patch
class TestCache(unittest.TestCase):
#patch("utils.sample.input", return_value='a')
def test_take_input(self, mock_input):
ans = take_input()
self.assertEqual(ans, 'Correct')
Take a note on how the patch is written and the input function is being called within the patch along with the specified return_value.

Related

Import module AND from module import in the same import

I'm trying to generate a way of storing and retriving results throughout my code.
Solution should:
Be able to store results (partial or final) easily, from any module in the code.
Should be nice beign able to retrive those results easily, from any routine in the code.
Be able to store results in any format (mainly DataFrames, Series, np arrays and custom objects).
Allow me to bypass data calculation to focus on output development.
My initial solution consisted on something of this sort:
output.py:
import pickle
results = {}
def store_res(result_key, result_data):
"""Store result for output processing."""
if result_key in results:
raise Exception('Key is already in dictionary')
results[result_key] = result_data
def load_previous_res():
"""Load previously computed results for output processing."""
global results
results = pickle.load(open('results/results.pkl', 'rb'))
main.py:
import pickle
from output import load_previous_res, results, store_res
def process_data():
some_result = 42
store_result('some_valuable_number', some_result)
[...]
# After all routines have been performed
pickle.dump(results, open('results/results.pkl', 'wb'))
def main():
response = input('Load and use the last computed results? [(Y)es, (N)o]')
if response.lower() not in ['yes', 'y']:
#Perform data processing and store resutls in pickle file
process_data()
else:
load_previous_res() # <-- THIS IS NOT WORKING AS INTENDED
Results are saved and pickled correctly, but when I run the program again and I want to bypass the calculations and just load the results. I except to get the same "results" that was pickled but I'm getting an empty dictionary.
If I change my import statemets (and calls) in main.py to
import pickle
from output import store_res
import output
def process_data():
some_result = 42
store_result('some_valuable_number', some_result)
[...]
# After all routines have been performed
results.
pickle.dump(output.results, open('results/results.pkl', 'wb'))
def main():
response = input('Load and use the last computed results? [(Y)es, (N)o]')
if response.lower() not in ['yes', 'y']:
#Perform data processing and store results in pickle file
process_data()
else:
output.load_previous_res() # <-- THIS WORKS AS INTENDED
I don't know why original solution doesn't work, and also I don't know if importing both a module and a function of that module in the same import block is correct.
Sorry for the long quesiton!

Run a dynamic multi-line string of code in python at runtime

How can I run a multi-line string of code from inside python, additionally the string is generated at runtime and stored in either a string or an array.
Background
I have a function which dynamically generates random code snippets in high volume.
I wish to pass this code to a variable/string and evaluate syntax/return code.
Code can not be imported from file/api request etc as focus is on high throughput and speed.
Example
# my function returns random code snippets
myCode = myRandomCodeGenerator()
# I wish to perform some in fight validation (ideally RC or syntax)
someTestingFunction(myCode)
My attempts so far
I have seen solutions such as the one below, however since my code is generated dynamically I have issues formatting. I have tried generating \n and \r or adding """ in the string to compensate with no luck.
code = """
def multiply(x,y):
return x*y
print('Multiply of 2 and 3 is: ',multiply(2,3))
"""
exec(code)
I have also tried using the call function on a string yields similar formatting issues.
So far the best I can do is to perform a line by line syntax check and feed it a line at a time as shown below.
import codeop
def is_valid_code(line):
try:
codeop.compile_command(line)
except SyntaxError:
return False
else:
return True
I suspect there may be some syntax tricks I could apply to preserve formatting of indentation and returns. I am also aware of the risk of generating dynamic code in runtime, and have a filter of allowed terms in the function.
Another way instead of eval and exec is to compile the code before and then to exec it:
Example:
import contextlib,sys
from io import StringIO
#contextlib.contextmanager
def stdoutIO(stdout=None):
old = sys.stdout
if stdout is None:
stdout = StringIO()
sys.stdout = stdout
yield stdout
sys.stdout = old
def run_code(override_kale_blocks):
compiled123 = []
for b123 in override_kale_blocks:
compiled123.append(compile(b123,"<string>","exec"))
with stdoutIO() as s:
for c123 in compiled123:
exec(c123)
return s.getvalue()
block0='''
import time
a=5
b=6
b=a+b
'''
block1='''
b=a+b
'''
block2="print(b)"
blocksleep='''
print('startsleep')
time.sleep(1)
print('donesleep')
'''
pc = (block0,blocksleep,block1,block2)
cb = []
print('before')
output= run_code(pc)
print(output)
print('after')
print("Hello World!\n")
source:
https://stackoverflow.com/a/3906390/1211174
Question was answered by Joran Beasley.
Essentially, my issue was I was adding a space after \n as other compilers get confused, python however takes this into account.
Thus if you add a space after a return carriage in a def statement, it will produce syntax error.
I have kept this incase others encounter similar issues.
Try using '\t' instead of placing tabs and spaces for the indentation, that may preserve your formatting.
code = """
def multiply(x,y):
\t return x*y
print('Multiply of 2 and 3 is: ',multiply(2,3))
"""
exec(code)

Access variables and lists from function

I am new to unit testing with Python. I would like to test some functions in my code. In particular I need to test if the outputs have specific dimensions or the same dimensions.
My Python script for unit testing looks like this:
import unittest
from func import *
class myTests(unittest.TestCase):
def setUp(self):
# I am not really sure whats the purpose of this function
def test_main(self):
# check if outputs of the function "main" are not empty:
self.assertTrue(main, msg = 'The main() function provides no return values!')
# check if "run['l_modeloutputs']" and "run['l_modeloutputs']", within the main() function have the same size:
self.assertCountEqual(self, run['l_modeloutputs'], run['l_dataoutputs'], msg=None)
# --> Doesn't work so far!
# check if the dimensions of "props['k_iso']", within the main() function are (80,40,100):
def tearDown(self):
# I am also not sure of the purpose of this function
if _name__ == "__main__":
unittest.main()
Here is the code under test:
def main(param_file):
# Load parameter file
run, model, sequences, hydraulics, flowtrans, elements, mg = hu.model_setup(param_file)
# some other code
...
if 'l_modeloutputs' in run:
if hydraulics['flag_gen'] is False:
print('No hydraulic parameters generated. No model outputs saved')
else:
save_models(realdir, realname, mg, run['l_modeloutputs'], flowtrans, props['k_iso'], props['ktensors'])
I need to access the parameters run['l_modeloutputs'] and run['l_dataoutputs'] of the main function from func.py. How can I pass the dimensions of these parameters to the unit testing script?
It sounds a bit like one of two things at the moment. Either your code isn't laid out at the moment in a way that is easy to test, or maybe you are trying to test or call too much code in one go.
If your code is laid out like the following:
main(file_name):
with open(file_name) as file:
... do work ...
results = outcome_of_work
and you are trying to test what you have got from the file_name as well as the size of results, then you may want to think of refactoring this so that you can test a smaller action. Maybe:
main(file_name):
# `get_file_contents` appears to be `hu.model_setup`
# `file_contents` would be `run`
file_contents = get_file_contents(file_name)
results = do_work_on_file_contents(file_contents)
Of course, if you already have a similar setup then the following is also applicable. This you can do easier tests, as you have easy control to both what's going into test (file_name or file_contents) and can then test the outcome (file_contents or results) for expected results.
With the unittest module you would basically be creating a small function for each test:
class Test(TestCase):
def test_get_file_contents(self):
# ... set up example `file-like object` ...
run = hu.model_setup(file_name)
self.assertCountEqual(
run['l_modeloutputs'], run['l_dataoutputs'])
... repeat for other possible files ...
def test_do_work_on_file_contents(self):
example_input = ... setup input ...
example_output = do_work_on_file_contents(example_input)
assert example_output == as_expected
This can then be repeated for different sets of potential inputs, both good and edge cases.
Its probably worth looking about for a more in-depth tutorial as this is obviously only a very quick look over.
And setUp and tearDown are only needed if there is something to be done for each test you have written (i.e. you have set up an object in a particular way, for several tests, this can be done in setUp and its run before each test function.

Python unit test advice

Can I get some advice on writing a unit test for the following piece of code?
%python
import sys
import json
sys.argv = []
sys.argv.append('{"product1":{"brand":"x","type":"y"}}')
sys.argv.append('{"product1":{"brand":"z","type":"a"}}')
products = sys.argv
yy= {}
my_products = []
for n, i in enumerate(products[:]):
xx = json.loads(i)
for j in xx.keys():
yy["brand"] = xx[j]['brand']
yy["type"] = xx[j]["type"]
my_products.append(yy)
print my_products
As it stands there aren't any units to test!!!
A test might consist of:
packaging your program in a script
invoking your program from python unit test as a subprocess
piping the output of your command process to a buffer
asserting the buffer is what you except it to be
While the above would technically allow you to have an automated test on your code it comes with a lot of burden:
- multi processing
- weak assertions by not having types
- coarse interaction (have to invoke a script, can't just assert on the brand/type logic
One way to address those issues could be to package your code into smaller units, ie create a method to encapsulate:
for j in xx.keys():
yy["brand"] = xx[j]['brand']
yy["type"] = xx[j]["type"]
my_products.append(yy)
Import it, exercise it and assert on its output. Then there might be something to map the loading and application of xx.keys() loop to an array (which you could also encapsulate as a function).
And then there could be the highest level taking in args and composing the product mapper loader transformer. And since your code will be thoroughly unit tested at this point, you may get away with not having a test for your top level script?

Unit testing functions that access files

I have two functions—one that builds the path to a set of files and another that reads the files. Below are the two functions:
def pass_file_name(self):
self.log_files= []
file_name = self.path+"\\access_"+self.appliacation+".log"
if os.path.isfile(file_name):
self.log_files.append(file_name)
for i in xrange(7):
file_name = self.path+"\\access_"+self.appliacation+".log"+"."+str(i+1)
if os.path.isfile(file_name):
self.log_files.append(file_name)
return self.log_files
def read_log_files (self, log_file_names):
self.log_entrys = []
self.log_line = []
for i in log_file_names:
self.f = open(i)
for line in self.f:
self.log_line = line.split(" ")
#print self.log_line
self.log_entrys.append(self.log_line)
return self.log_entrys
What would be the best way to unit test these two functions?
You have two units here:
One that generate file paths
Second that reads them
Thus there should be two unit-test-cases (i.e. classes with tests). First would test only file paths generation. Second would test reading from predefined set of files you prepared in special subdirectory of tests directory, it should test in isolation from first test case.
In your case, you could probably have very short log files for tests. In this case for better readability and maintenance it is good idea to embed them right in test code. But in this case you'll have to improve your reading function a bit so it can take either file name or file-like object:
from cStringIO import StringIO
# ...
def test_some_log_reading_scenario(self):
log1 = '\n'.join([
'log line',
'another log line'
])
log2 = '\n'.join([
'another log another line',
'lala blah blah'
])
# ...
result = myobj.read_log_files([StringIO(log1), StringIO(log2)])
# assert result
Personally, I'd build a test harness that set up the required files before testing those two functions.
For each test case (where you expect the file to be present - remember to test failure cases too!), write some known logs into the appropriately named files; then call the functions under test and check the results.
I'm no expert but I'll give it a go. First a bit of refactoring: make them functional (remove all class stuff), remove unneeded things. This should make it much easier to test. You can always make the class call these functions if you really want it in a class.
def pass_file_name(base_filename, exists):
"""return a list of filenames that exist
based upon `base_filename`.
use `os.path.isfile` for `exists`"""
log_files = []
if exists(base_filename):
log_files.append(base_filename)
for i in range(1, 8):
filename = base_filename + "." + str(i)
if exists(filename):
log_files.append(filename)
return log_files
def read_log_files (self, log_files):
"""read and parse each line from log_files
use `pass_file_name` for `log_files`"""
log_entrys = []
for filename in log_files:
with open(filename) as myfile:
for line in myfile:
log_entrys.append(line.split())
return log_entrys
Now we can easily test pass_file_name by passing in a custom function to exists.
class Test_pass_file_name(unittest.TestCase):
def test_1(self):
"""assume every file exists
make sure all logs file are there"""
exists = lambda _: True
log_files = pass_file_name("a", exists)
self.assertEqual(log_files,
["a", "a.1", "a.2", "a.3",
"a.4", "a.5", "a.6", "a.7"])
def test_2(self):
"""assume no files exists
make sure nothing returned"""
exists = lambda _: False
log_files = pass_file_name("a", exists)
self.assertEqual(log_files, [])
# ...more tests here ...
As we assume os.path.isfile works we should have got pretty good testing of the first function. Though you could always have the test actually create some files then call pass_file_name with exists = os.path.isfile.
The second one is harder to test; I have been told that the best (unit)tests don't touch the network, databases, GUI or the hard-drive. So maybe some more refactoring would make it easier. Mocking open could work; or would could actually write some long file in the test function and read them in.
How do I mock an open used in a with statement (using the Mock framework in Python)?
Bind the open name in the module to a function that mocks the file opening.

Categories