Voici la fonction que je veux tester:
@mock.patch.object(demo_sequence.lib, 'conn') def test_send_something(mock_conn): mock_conn.SSH1.recbuf = 'home' assert demo_sequence.lib.PASS == demo_sequence.send_something() mock_conn.SSH1.send.assert_any_call('ls\n') mock_conn.SSH1.send.assert_any_call('whoami\n')
Chaque fois que j'appelle conn.send (), la sortie de l'appel est stockée dans conn.recbuf. Pour tester cela, j'ai besoin que conn.recbuf soit différent à chaque fois qu'il est appelé. (Je sais que je pourrais simplement changer conn.recbuf pour être une chaîne contenant à la fois home et USER mais cela ne fonctionnera pas pour des fonctions plus compliquées)
Voici ce que j'ai trouvé jusqu'ici dans mon test:
def send_something(): conn = lib.conn.SSH1 conn.open() conn.send('ls\n') if 'home' not in conn.recbuf: return lib.FAIL conn.send('whoami\n') if USER not in conn.recbuf: return lib.FAIL return lib.PASS
Ceci échoue évidemment car conn.recbuf est uniquement «home» et ne contient pas USER.
J'ai besoin de quelque chose comme conn.recbuf.side_effect = ['home', USER] mais cela fonctionnera en faisant simplement référence à recbuf sans l'appeler.
3 Réponses :
Ce que vous recherchez est side_effect
: https://docs.python.org/3/library/unittest.mock.html
Disons que mock
est votre objet fictif. Ensuite, vous pouvez faire ceci:
mock() # -> a mock() # -> b mock() # -> c
puis chaque fois que vous appelez mock
, la valeur suivante de la liste est renvoyée.
mock.side_effect = [a,b,c]
Downvoté parce que c'est exactement ce qui est encore en apprentissage mentionné dans sa dernière phrase et a également expliqué pourquoi cela ne fonctionne pas: il a besoin exactement de ce comportement pour les attributs et non les callables.
Je l'ai. Vous pouvez réaliser la même chose pour les attributs en vous moquant de la méthode __getattr__
mais cela peut être un peu compliqué.
Pouvez-vous expliquer comment vous moquer de getattr pour trouver une solution à mon problème?
Lorsque vous faites a.x, il est traduit en .__ getattr __ ('x'). Donc, vous pouvez essayer de vous moquer de cela.
Si vous pouvez changer conn.recbuf en propriété , vous pouvez utiliser un PropertyMock pour obtenir l'effet souhaité.
from unittest import mock class Dummy: def __init__(self, myattribute): self._myattribute = myattribute @property def myattribute(self): return self._myattribute def test(): with mock.patch('__main__.Dummy.myattribute', new_callable=mock.PropertyMock) as mocked_attribute: mocked_attribute.side_effect = [4,5,6] d = Dummy("foo") print(d.myattribute) print(d.myattribute) print(d.myattribute)
Cependant, pour votre problème réel, je pense que les commentaires à votre question semblent inclure des approches raisonnables.
Cette approche répond à mon besoin. J'ai vu quelques commentaires et réponses précédentes qui disaient d'utiliser PropertyMock mais je n'ai jamais vérifié si recbuf était une propriété immobilière. (a le décorateur de la propriété). Merci pour l'explication supplémentaire.
J'étais aux prises avec un problème similaire plus tôt et j'ai trouvé une solution en utilisant le PropertyMock
. Dans mon cas, je voulais stocker une valeur ailleurs dans le cas de test pour m'affirmer en appelant une méthode via un side_effect
lorsqu'une propriété était définie sur la classe dont je me moquais.
Je pense que cela devrait fonctionne également pour votre cas (si j'ai compris ce que vous essayez de faire).
La solution consiste à utiliser un PropertyMock
pour votre propriété recbuf et mettez le
side_effect
dedans:
from unittest.mock import MagicMock, PropertyMock conn = MagicMock() type(conn).recbuf = PropertMock(side_effect=['home', USER])
L'effet secondaire sera appelé chaque fois que la propriété recbuf
est accédé, dans ce cas itération via ['home', USER]
à chaque fois. Je suppose que vous devrez également corriger cette valeur USER
pour ne pas avoir d'erreur de nom.
J'espère que cela fonctionne / aide!
Ne voudriez-vous pas simplement vous moquer de
envoyer
pour qu'il ne fasse rien d'autre que de mettre à jourrecbuff
? Il est difficile de fournir une bonne solution sans savoir quel est réellement le type de `conn`.La bibliothèque conn est bizarre (mauvaise). SSH1 doit également être ridiculisé car il n'existe pas toujours. C'est pourquoi j'ai choisi de se moquer du niveau lib.conn.
Je pourrais aussi recommander qu'au lieu de coder en dur la valeur de
conn
danssend_something
, vous en fassiez un argument de fonction. Ensuite, vous pouvez passer n'importe quel objet de votre choix comme argument au lieu d'avoir à utilisermock.patch
du tout.Si vous testez unitairement send_something, il est probablement préférable de se moquer entièrement de conn. Il est également possible de faire un autre type de test spécial pour la bibliothèque lib.conn si vous voulez voir qu'elle remplit le contrat. En outre, il est possible de créer un shim / adaptateur autour de la "mauvaise" bibliothèque - si votre code utilise la bibliothèque à plusieurs endroits.