3
votes

comment utiliser builtins.input pour plusieurs entrées

Dans mon unittest, j'ai 2 invites dans le test. J'essaie d'utiliser 2 @patch ("builtins.input") , mais il semble ne prendre que le 1 des valeurs de retour.

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

L'appel a.setProfileName () demandera 1 entrée en utilisant input () appel dans ma fonction. Dans ce test, il appellera a.setProfileName () deux fois.

  • La première fois que j'appelle a.setProfileName () , je saisis la valeur de self.profileName_prod .
  • La deuxième fois que je l'appelle, je saisis la valeur de self.profileName_dev .

Mais le test échoue après le deuxième cas a.setProfileName () (à l'avant-dernier assertEqual après le deuxième a.setProfileName () call).

@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)

La raison de l'échec est que a.getProfileName renvoie la valeur de self .profileName_dev au lieu de self.profileName_prod.

J'avais testé mon code dans le cli python pour m'assurer que le comportement est correct.

Tout commentaire est apprécié.

Merci les gars!


3 commentaires

Vous ne pouvez pas patcher la même chose deux fois. Au lieu de cela, faites en sorte que l'objet patch renvoie la première valeur, puis la seconde lors d'appels successifs.


setProfileName doit prendre un argument; laissez le code plus proche du «bord» de votre programme être responsable de l'obtention des entrées de l'utilisateur. Moins il y a d'E / S dans votre code, mieux c'est.


Autrement dit, préférez l'équivalent de a.setProfileName (input ()) à def setProfileName (self): x = input (); ... .


3 Réponses :


5
votes

Patcher deux fois la même fonction ne la fait pas renvoyer des valeurs différentes sur différents appels. Vous pouvez utiliser l'attribut side_effect de l'objet Mock en le définissant avec une liste de valeurs que la fonction doit renvoyer lors d'appels successifs à la place:

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


3 commentaires

Merci blhsing. L'objectif de mon test serait de m'assurer que le comportement de la fonction gère correctement l'entrée. Si je me moque, ce ne serait pas un vrai test de la fonction.


Ma solution suggérée n'est pas différente de votre tentative de solution en termes de ce qui est moqué. Autrement dit, dans nos deux solutions, seule la fonction input intégrée est moquée, donc je ne vois pas comment vous le trouvez concernant le input function est simulé dans ma solution.


Mon mauvais, blhsing. Je ne savais pas comment fonctionnait side_effect et j'ai mal compris ce qu'il faisait. J'ai testé votre solution avec mon code, et ça marche! Beaucoup plus élégant. Merci! Mise à jour de la réponse.



1
votes

J'ai revisité la solution de blhsing, et elle est beaucoup plus élégante. Voici mon code de test fonctionnel maintenant:

@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)

Merci à tous pour vos commentaires! :)


0 commentaires

1
votes

Pour fournir une réponse plus simple et précise à toute personne visitant ce site en 2020 et plus tard, vous pouvez simplement faire

`
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()
`

en python 3.8 plus tard.

Pour voir un exemple complet et facile à suivre, continuez à lire sinon arrêtez-vous ici.

Exemple complet: Le code ci-dessous montre un exemple complet de ceci avec une classe nommée "AnsweredQuestion" et avec un test unitaire

`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
`

Test unitaire pour tester la classe ci-dessus AnsweredQuestion

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


1 commentaires

Heureux de vous voir poster des réponses à d'anciennes questions! Gardez à l'esprit que vous devez ajouter des détails sur ce que vous avez corrigé ou pourquoi il est correct. Il aide les utilisateurs à comprendre ce qui se passe, plutôt que de simplement recevoir la réponse. Considérez cela comme une règle pour toutes vos réponses ici. J'espère que tu as du bon temps.