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.

Asked By: noisoyhermando

||

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()
Answered By: Wolric

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…

Answered By: Cpt.Hook

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')
Answered By: Sezer BOZKIR