Es wurde behauptet, dass eine Funktion / Methode nicht mit Mock aufgerufen wurde

131

Ich verwende die Mock-Bibliothek, um meine Anwendung zu testen, möchte jedoch behaupten, dass eine Funktion nicht aufgerufen wurde. Mock-Dokumente sprechen über Methoden wie mock.assert_called_withund mock.assert_called_once_with, aber ich habe nichts Ähnliches mock.assert_not_calledoder ähnliches gefunden, um zu überprüfen, ob Mock NICHT aufgerufen wurde .

Ich könnte mit so etwas wie dem folgenden gehen, obwohl es weder cool noch pythonisch erscheint:

def test_something:
    # some actions
    with patch('something') as my_var:
        try:
            # args are not important. func should never be called in this test
            my_var.assert_called_with(some, args)
        except AssertionError:
            pass  # this error being raised means it's ok
    # other stuff

Irgendwelche Ideen, wie man das erreicht?

Gerard
quelle
Wie @Ahmet in seiner Antwort hervorhebt, wird assert_not_called jetzt auch im Backport ( docs.python.org/3/library/… ) unterstützt.
Martin

Antworten:

144

Dies sollte für Ihren Fall funktionieren.

assert not my_var.called, 'method should not have been called'

Stichprobe;

>>> mock=Mock()
>>> mock.a()
<Mock name='mock.a()' id='4349129872'>
>>> assert not mock.b.called, 'b was called and should not have been'
>>> assert not mock.a.called, 'a was called and should not have been'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: a was called and should not have been
Joachim Isaksson
quelle
Benötigt diese Antwort Django? Ich erhalte eine Fehlermeldung:AttributeError: MockCallable instance has no attribute 'called'
Nathan Arthur
@ NathanArthur Hm, ich glaube nicht, nach sudo easy_install -U mockund from mock import Mockunter MacOS läuft das oben genannte reibungslos. Nie Django installiert :)
Joachim Isaksson
Hmm. Das ist seltsam. Ich verwende Python 2.7.1 und verwende unittest und from mock import MockPython Mock 0.1.0 für meine Tests. Klingt irgendetwas davon problematisch?
Nathan Arthur
Ich verspotte eine aufrufbare Klasse aus einem anderen Modul. Können module_to_test.another_module.class = mock.Mock()Sie also bestätigen , dass Sie sich nicht an Aufrufe in verschiedenen Testfällen (unittest.TestCase-Instanzen) erinnern können? Ich denke, die Anzahl der
Anrufe wird
66

Obwohl es sich um eine alte Frage handelt, möchte ich hinzufügen, dass die aktuelle mockBibliothek (Backport von unittest.mock) die assert_not_calledMethode unterstützt .

Aktualisieren Sie einfach Ihre;

pip install mock --upgrade

Ahmet
quelle
29

Sie können das calledAttribut überprüfen. Wenn Ihre Behauptung jedoch fehlschlägt, sollten Sie als Nächstes etwas über den unerwarteten Anruf wissen. Sie können also auch veranlassen, dass diese Informationen von Anfang an angezeigt werden. Mit unittestkönnen Sie call_args_liststattdessen den Inhalt überprüfen von :

self.assertItemsEqual(my_var.call_args_list, [])

Wenn es fehlschlägt, wird folgende Meldung angezeigt:

AssertionError: Die Anzahl der Elemente war nicht gleich:
Erstens hat 0, zweitens hat 1: call ('erstes Argument', 4)
Rob Kennedy
quelle
14

Wenn Sie mit class testen, erbt unittest.TestCase einfach Methoden wie:

  • assertTrue
  • assertFalse
  • assertEqual

und ähnliches (in der Python-Dokumentation finden Sie den Rest).

In Ihrem Beispiel können wir einfach behaupten, ob die Eigenschaft mock_method.called False ist , was bedeutet, dass die Methode nicht aufgerufen wurde.

import unittest
from unittest import mock

import my_module

class A(unittest.TestCase):
    def setUp(self):
        self.message = "Method should not be called. Called {times} times!"

    @mock.patch("my_module.method_to_mock")
    def test(self, mock_method):
        my_module.method_to_mock()

        self.assertFalse(mock_method.called,
                         self.message.format(times=mock_method.call_count))
Hunter_71
quelle
10

Mit können python >= 3.5Sie verwenden mock_object.assert_not_called().

Valex
quelle
1

Nach anderen Antworten zu urteilen, sprach niemand außer @ rob-kennedy über das call_args_list.

Es ist ein mächtiges Werkzeug, mit dem Sie genau das Gegenteil von implementieren können MagicMock.assert_called_with()

call_args_listist eine Liste von callObjekten. Jedes callObjekt repräsentiert einen Aufruf eines verspotteten Callable.

>>> from unittest.mock import MagicMock
>>> m = MagicMock()
>>> m.call_args_list
[]
>>> m(42)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42)]
>>> m(42, 30)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42), call(42, 30)]

Das Konsumieren eines callObjekts ist einfach, da Sie es mit einem Tupel der Länge 2 vergleichen können, wobei die erste Komponente ein Tupel ist, das alle Positionsargumente des zugehörigen Aufrufs enthält, während die zweite Komponente ein Wörterbuch der Schlüsselwortargumente ist.

>>> ((42,),) in m.call_args_list
True
>>> m(42, foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((42,), {'foo': 'bar'}) in m.call_args_list
True
>>> m(foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((), {'foo': 'bar'}) in m.call_args_list
True

Ein Weg, um das spezifische Problem des OP anzugehen, ist also

def test_something():
    with patch('something') as my_var:
        assert ((some, args),) not in my_var.call_args_list

Beachten Sie, dass Sie auf diese Weise, anstatt nur zu überprüfen, ob ein verspotteter Callable aufgerufen wurde, über MagicMock.calledüberprüfen können, ob er mit einem bestimmten Satz von Argumenten aufgerufen wurde.

Das ist nützlich. Angenommen, Sie möchten eine Funktion testen, die eine Liste erstellt, und eine andere Funktion aufrufen.compute() , für jeden Wert der Liste nur dann wenn sie eine bestimmte Bedingung erfüllen.

Sie können jetzt verspotten computeund testen, ob ein Wert aufgerufen wurde, andere jedoch nicht.

Giuseppe Crinò
quelle