How could I use 'assert' and a variable 'actual' to write a test code for a user input code for the conversion of time?
Question:
`
def conversion():
options = print('Would you like to convert hours to mins, or mins to hours?')
choice = input()
if choice == 'hours to mins':
hours = int(input('How many hours? '))
mins = hours * 60
print(mins, 'Minutes')
elif choice == 'mins to hours':
mins = int(input('How many minutes? '))
hours = mins/60
print(hours, 'Hours')
else:
print('An error has occured')
conversion()
This is the production code which is meant to be used to write a corresponding test code. `
I am unsure on how to go about writing a test code using ‘siminput’ ‘assert’ and the a variable ‘actual’ to write a working test code for the line of code above for it to properly run in unittest.
Answers:
The function conversion
doesn’t take arguments so your test conditions to use siminput
and actual
as test variables won’t work.
As your function only calls print
instead of actually returning a value, you could use patch
from the unittest.mock
module to assess if print
was called with the correct value.
See below a test example for when user inputs as choice = "hours to mins"
and hours = 1
:
from unittest.mock import patch
@patch("builtins.print")
def test_conversion(mock_print):
conversion()
mock_print.assert_called_with(60, "Minutes")
test_conversion()
You can use pytest with the pytest-mock extension. Install them via pip or conda, or whatever you use.
Quick Fix
First I made a small change to your code to make it a bit easier to test: I added a return statement. Now the code will also return the result.
# conversion.py
def conversion():
print('Would you like to convert hours to mins, or mins to hours?')
choice = input()
if choice == 'hours to mins':
hours = int(input('How many hours? '))
mins = hours * 60
print(mins, 'Minutes')
return mins
elif choice == 'mins to hours':
mins = int(input('How many minutes? '))
hours = mins/60
print(hours, 'Hours')
return hours
else:
print('An error has occured')
return False
Ok, now we create a test
# conversion_test.py
def test_hrs_to_min(mocker):
input_provider = mocker.patch('builtins.input')
# This line is crucial: You configer the values each call to `Input` will return.
input_provider.side_effect = ['hours to mins', '3']
result = conversion()
assert result == 3*60
when we run this now with pytest -s
from the comannd line, we see the result.
You can also mock the builtin.print
and check if it was called with the right arguments (mock_print.assert_called_with(3*60, "Minutes")
.
See Mocking examples for further details.
Better Solution
As already meantioned it’d be a good idea to sepereate concerns in your code.
def conversion():
print('Would you like to convert hours to mins, or mins to hours?')
choice = input()
if choice == 'hours to mins':
hours = int(input('How many hours? '))
print(hrs2mins(hours), 'Minutes')
elif choice == 'mins to hours':
mins = int(input('How many minutes? '))
print(min2hrs(mins), 'Hours')
print('An error has occured')
return False
def hrs2mins(hrs: int) -> int:
return hrs * 60
def min2hrs(mins: int) -> float:
return mins/60
now you can test the "business logic" (the conversion) seperatly from the User interface…
test_input.py:
def conversion():
print("Would you like to conver...")
choice = input()
if choice == 'hour to mins':
hours = int(input("How many hours?"))
mins = hours * 60
print(mins, "Minutes")
else:
print('An error has occured')
test_conversion.py:
from unittest import mock
from unittest import TestCase
from test_input import conversion
from io import StringIO
class ConversionTest(TestCase):
@mock.patch('test_input.input', create=True)
def test_minutes(self, mocked_input):
mocked_input.side_effect = ["hour to mins", 4]
with mock.patch('sys.stdout', new=StringIO()) as fake_out:
conversion()
output = fake_out.getvalue()
self.assertEqual(output.replace("n", ""), 'Would you like to conver...240 Minutes')
`
def conversion():
options = print('Would you like to convert hours to mins, or mins to hours?')
choice = input()
if choice == 'hours to mins':
hours = int(input('How many hours? '))
mins = hours * 60
print(mins, 'Minutes')
elif choice == 'mins to hours':
mins = int(input('How many minutes? '))
hours = mins/60
print(hours, 'Hours')
else:
print('An error has occured')
conversion()
This is the production code which is meant to be used to write a corresponding test code. `
I am unsure on how to go about writing a test code using ‘siminput’ ‘assert’ and the a variable ‘actual’ to write a working test code for the line of code above for it to properly run in unittest.
The function conversion
doesn’t take arguments so your test conditions to use siminput
and actual
as test variables won’t work.
As your function only calls print
instead of actually returning a value, you could use patch
from the unittest.mock
module to assess if print
was called with the correct value.
See below a test example for when user inputs as choice = "hours to mins"
and hours = 1
:
from unittest.mock import patch
@patch("builtins.print")
def test_conversion(mock_print):
conversion()
mock_print.assert_called_with(60, "Minutes")
test_conversion()
You can use pytest with the pytest-mock extension. Install them via pip or conda, or whatever you use.
Quick Fix
First I made a small change to your code to make it a bit easier to test: I added a return statement. Now the code will also return the result.
# conversion.py
def conversion():
print('Would you like to convert hours to mins, or mins to hours?')
choice = input()
if choice == 'hours to mins':
hours = int(input('How many hours? '))
mins = hours * 60
print(mins, 'Minutes')
return mins
elif choice == 'mins to hours':
mins = int(input('How many minutes? '))
hours = mins/60
print(hours, 'Hours')
return hours
else:
print('An error has occured')
return False
Ok, now we create a test
# conversion_test.py
def test_hrs_to_min(mocker):
input_provider = mocker.patch('builtins.input')
# This line is crucial: You configer the values each call to `Input` will return.
input_provider.side_effect = ['hours to mins', '3']
result = conversion()
assert result == 3*60
when we run this now with pytest -s
from the comannd line, we see the result.
You can also mock the builtin.print
and check if it was called with the right arguments (mock_print.assert_called_with(3*60, "Minutes")
.
See Mocking examples for further details.
Better Solution
As already meantioned it’d be a good idea to sepereate concerns in your code.
def conversion():
print('Would you like to convert hours to mins, or mins to hours?')
choice = input()
if choice == 'hours to mins':
hours = int(input('How many hours? '))
print(hrs2mins(hours), 'Minutes')
elif choice == 'mins to hours':
mins = int(input('How many minutes? '))
print(min2hrs(mins), 'Hours')
print('An error has occured')
return False
def hrs2mins(hrs: int) -> int:
return hrs * 60
def min2hrs(mins: int) -> float:
return mins/60
now you can test the "business logic" (the conversion) seperatly from the User interface…
test_input.py:
def conversion():
print("Would you like to conver...")
choice = input()
if choice == 'hour to mins':
hours = int(input("How many hours?"))
mins = hours * 60
print(mins, "Minutes")
else:
print('An error has occured')
test_conversion.py:
from unittest import mock
from unittest import TestCase
from test_input import conversion
from io import StringIO
class ConversionTest(TestCase):
@mock.patch('test_input.input', create=True)
def test_minutes(self, mocked_input):
mocked_input.side_effect = ["hour to mins", 4]
with mock.patch('sys.stdout', new=StringIO()) as fake_out:
conversion()
output = fake_out.getvalue()
self.assertEqual(output.replace("n", ""), 'Would you like to conver...240 Minutes')