J'ai du mal à gérer le tuple imbriqué qui Mock.call_args_list
résultats.
def test_foo(self):
def foo(fn):
fn('PASS and some other stuff')
f = Mock()
foo(f)
foo(f)
foo(f)
for call in f.call_args_list:
for args in call:
for arg in args:
self.assertTrue(arg.startswith('PASS'))
Je voudrais savoir s'il existe une meilleure façon de décompresser cette call_args_list sur l'objet factice afin de faire mon affirmation. Cette boucle fonctionne, mais il semble qu'il doit y avoir un moyen plus simple.
Je pense que beaucoup de difficultés ici sont résumées dans le traitement de l'objet "appel". Il peut être considéré comme un tuple avec 2 membres (args, kwargs)
et il est donc souvent agréable de le déballer:
args, kwargs = call
Une fois décompressé, vous pouvez faire vos affirmations séparément pour les arguments et les kwargs (car l'un est un tuple et l'autre un dict)
def test_foo(self):
def foo(fn):
fn('PASS and some other stuff')
f = Mock()
foo(f)
foo(f)
foo(f)
for call in f.call_args_list:
args, kwargs = call
self.assertTrue(all(a.startswith('PASS') for a in args))
Notez que parfois la concision n'est pas utile (par exemple en cas d'erreur):
for call in f.call_args_list:
args, kwargs = call
for a in args:
self.assertTrue(a.startswith('PASS'), msg="%s doesn't start with PASS" % a)
Une meilleure façon pourrait être de construire vous-même les appels attendus, puis d'utiliser une assertion directe:
>>> from mock import call, Mock
>>> f = Mock()
>>> f('first call')
<Mock name='mock()' id='31270416'>
>>> f('second call')
<Mock name='mock()' id='31270416'>
>>> expected_calls = [call(s + ' call') for s in ('first', 'second')]
>>> f.assert_has_calls(expected_calls)
Notez que les appels doivent être séquentiels, si vous ne le souhaitez pas, remplacez le any_order
kwarg à l'assertion.
Notez également qu'il est autorisé qu'il y ait des appels supplémentaires avant ou après les appels spécifiés. Si vous ne le souhaitez pas, vous devrez ajouter une autre assertion:
>>> assert f.call_count == len(expected_calls)
En réponse au commentaire de mgilson, voici un exemple de création d'un objet factice que vous pouvez utiliser pour les comparaisons d'égalité génériques:
>>> class AnySuffix(object):
... def __eq__(self, other):
... try:
... return other.startswith('PASS')
... except Exception:
... return False
...
>>> f = Mock()
>>> f('PASS and some other stuff')
<Mock name='mock()' id='28717456'>
>>> f('PASS more stuff')
<Mock name='mock()' id='28717456'>
>>> f("PASS blah blah don't care")
<Mock name='mock()' id='28717456'>
>>> expected_calls = [call(AnySuffix())]*3
>>> f.assert_has_calls(expected_calls)
Et un exemple du mode de défaillance:
>>> Mock().assert_has_calls(expected_calls)
AssertionError: Calls not found.
Expected: [call(<__main__.AnySuffix object at 0x1f6d750>),
call(<__main__.AnySuffix object at 0x1f6d750>),
call(<__main__.AnySuffix object at 0x1f6d750>)]
Actual: []