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.