kiwo
kiwo

Reputation: 158

how to use builtins.input for multiple inputs

In my unittest, I have 2 prompts in the test. I am trying to use 2 @patch("builtins.input"), but it seems to only take the 1 of the return values.

@patch("builtins.input")
@patch("builtins.input")
def test_setProfileName_modify_init_prompt_empty(self, paramName1, paramName2):
    paramName1.return_value = self.profileName_prod
    paramName2.return_value = self.profileName_dev


    a = c.ALMConfig(self.configType)
    self.assertTrue(a.setProfileName())
    self.assertEqual(a.getProfileName(), self.profileName_dev)
    self.assertEqual(a.profileName, self.profileName_dev)


    self.assertTrue(a.setProfileName())
    self.assertEqual(a.getProfileName(), self.profileName_prod)
    self.assertEqual(a.profileName, self.profileName_prod)

The call a.setProfileName() will prompt for 1 input using input() call in my function. In this test, it will call a.setProfileName() twice.

But the test fails after the second a.setProfileName() case (at the second to last assertEqual after the second a.setProfileName() call).

    self.assertEqual(a.getProfileName(), self.profileName_prod)

The reason for the failure is because a.getProfileName is returning the value for self.profileName_dev instead of self.profileName_prod.

I had tested my code in the python cli to make sure the behavior is correct.

Any feedback is appreciated.

Thanks guys!

Upvotes: 3

Views: 3204

Answers (3)

TechGuyChris
TechGuyChris

Reputation: 21

To provide a more simple and to the point answer for anyone visiting this in 2020 and later, you can just do

`with patch("builtins.input", return_value = "Whatever you want returned"):
   self.assertEqual("<Object>", "Whatever you want returned")
`

in python 3.8 in later.

To see a full easy to follow example keep reading otherwise stop here.

Full Example: The below code shows a full example of this with a class named "AnsweredQuestion" and with a unit test

`class AnsweredQuestion:
   def __init__(self):
     print("I hope you find this example helpful")
   
   def get_input(self):
     print("Enter your input")
     entered_data = input()
     print("You entered '" + entered_data + "'")
     return get_input
`

Unit Test to test above class AnsweredQuestion

`
import builtins
import unittest
import sys
sys.path.append(".")
# Assuming a directory named "answers" in your setup
import answers
from answers import AnsweredQuestion
from unittest.mock import Mock, patch

class TestAnsweredQuestion(unittest.TestCase):
  def test_get_input(self):
    with patch("builtins.input", return_value = "Thanks. This is correct"):
      self.assertEqual(AnsweredQuestion.get_input(), "Thanks this is correct")


if __name__ == '__main__':
  unittest.main()
`

Upvotes: 1

kiwo
kiwo

Reputation: 158

I revisited blhsing's solution, and it is more much elegant. Here is my working test code now:

@patch('builtins.input', side_effect=['dev', 'production'])
def test_setProfileName_modify_init_prompt_update_new(self, paramName):
    a = c.ALMConfig(self.configType)
    self.assertTrue(a.setProfileName())
    self.assertEqual(a.getProfileName(), self.profileName_dev)
    self.assertEqual(a.profileName, self.profileName_dev)


    self.assertEqual(a.getProfileName(), self.profileName_dev)
    self.assertTrue(a.setProfileName())
    self.assertEqual(a.getProfileName(), self.profileName_prod)
    self.assertEqual(a.profileName, self.profileName_prod)

Thanks everyone for your comments! :)

Upvotes: 1

blhsing
blhsing

Reputation: 107124

Patching the same function twice does not make it return different values on different calls. You can use the side_effect attribute of the Mock object by setting it with a list of values you want the function to return in successive calls instead:

from unittest.mock import patch
@patch('builtins.input', side_effect=['dev', 'prod'])
def test_input(mock_input):
    assert input() == 'dev'
    assert input() == 'prod'
test_input() # this will not raise an exception since all assertions are True

Upvotes: 5

Related Questions