Asserire che una funzione / metodo non è stato chiamato usando Mock


131

Sto usando la libreria Mock per testare la mia applicazione, ma voglio affermare che alcune funzioni non sono state chiamate. I documenti falsi parlano di metodi come mock.assert_called_withe mock.assert_called_once_with, ma non ho trovato nulla di simile mock.assert_not_calledo qualcosa di correlato per verificare che il mock NON sia stato chiamato .

Potrei andare con qualcosa di simile al seguente, anche se non sembra bello né pitonico:

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

Qualche idea su come realizzare questo?


Come sottolinea @Ahmet nella sua risposta, assert_not_called è ora supportato, anche nel backport ( docs.python.org/3/library/… ).
Martin,

Risposte:


144

Questo dovrebbe funzionare per il tuo caso;

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

Campione;

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

Questa risposta richiede Django? Ricevo un errore:AttributeError: MockCallable instance has no attribute 'called'
Nathan Arthur,

@NathanArthur Hm, non credo, dopo sudo easy_install -U mocke from mock import Mocksu MacOS, quanto sopra funziona senza intoppi. Django mai installato :)
Joachim Isaksson,

Hmm. È strano. Sto eseguendo Python 2.7.1 e sto usando unittest e from mock import Mockcon Python Mock 0.1.0 per i miei test. Qualcuno di questo suona problematico?
Nathan Arthur,

Sto deridendo una classe richiamabile da un altro modulo, quindi sembra module_to_test.another_module.class = mock.Mock(), puoi confermare che questo non ricordi le chiamate attraverso diversi casi di test (istanze unittest.TestCase)? Penso che il conteggio delle chiamate non venga ripristinato in questo caso
0xc0de,

66

Sebbene sia una vecchia domanda, vorrei aggiungere che attualmente la mocklibreria (backport di unittest.mock) supporta il assert_not_calledmetodo.

Aggiorna il tuo;

pip install mock --upgrade


29

Puoi controllare l' calledattributo, ma se la tua affermazione fallisce, la prossima cosa che vorresti sapere è qualcosa sulla chiamata imprevista, quindi puoi anche organizzare che tali informazioni vengano visualizzate dall'inizio. Utilizzando unittest, puoi invece controllare il contenuto di call_args_list:

self.assertItemsEqual(my_var.call_args_list, [])

Quando fallisce, dà un messaggio come questo:

AssertionError: il conteggio degli elementi non era uguale:
Il primo ha 0, il secondo ha 1: call ('first argomento', 4)

14

Quando provi usando la classe eredita unittest.TestCase puoi semplicemente usare metodi come:

  • assertTrue
  • assertFalse
  • assertEqual

e simili (nella documentazione di Python trovi il resto).

Nel tuo esempio possiamo semplicemente affermare se la proprietà mock_method.called è False , il che significa che il metodo non è stato chiamato.

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

11

Con python >= 3.5te puoi usare mock_object.assert_not_called().


1

A giudicare dalle altre risposte, nessuno, tranne @ rob-kennedy, ha parlato del call_args_list.

È uno strumento potente per cui puoi implementare esattamente il contrario MagicMock.assert_called_with()

call_args_listè un elenco di calloggetti. Ogni calloggetto rappresenta una chiamata fatta su un callable deriso.

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

Il consumo di un calloggetto è facile, poiché è possibile confrontarlo con una tupla di lunghezza 2 in cui il primo componente è una tupla contenente tutti gli argomenti posizionali della chiamata correlata, mentre il secondo componente è un dizionario degli argomenti delle parole chiave.

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

Quindi, un modo per affrontare il problema specifico del PO è

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

Si noti che in questo modo, invece di verificare solo se è stato chiamato un callable deriso, tramite MagicMock.calledora è possibile verificare se è stato chiamato con un set specifico di argomenti.

Questo è utile Supponi di voler testare una funzione che accetta un elenco e chiama un'altra funzione compute(), per ciascuno dei valori dell'elenco solo se soddisfano una condizione specifica.

Ora puoi deridere computee testare se è stato chiamato su un valore ma non su altri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.