Menyatakan fungsi / metode tidak dipanggil menggunakan Mock

131

Saya menggunakan perpustakaan Mock untuk menguji aplikasi saya, tetapi saya ingin menegaskan bahwa beberapa fungsi tidak dipanggil. Doc mock berbicara tentang metode seperti mock.assert_called_withdan mock.assert_called_once_with, tapi saya tidak menemukan sesuatu seperti mock.assert_not_calledatau sesuatu yang berkaitan dengan memverifikasi tiruan TIDAK dipanggil .

Saya bisa menggunakan yang seperti ini, meskipun tidak keren atau pythonic:

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

Adakah ide bagaimana mencapainya?

Gerard
sumber
Seperti @Ahmet tunjukkan dalam jawabannya, assert_not_called sekarang didukung, juga di backport ( docs.python.org/3/library/… ).
Martin

Jawaban:

144

Ini harus bekerja untuk kasus Anda;

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

Sampel;

>>> 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
sumber
Apakah jawaban ini membutuhkan Django? Saya mendapat pesan kesalahan:AttributeError: MockCallable instance has no attribute 'called'
Nathan Arthur
@NathanArthur Hm, saya kira tidak, setelah sudo easy_install -U mockdan from mock import Mockpada MacOS, hal di atas berjalan tanpa hambatan. Tidak pernah menginstal Django :)
Joachim Isaksson
Hmm. Itu aneh. Saya menjalankan Python 2.7.1 dan saya menggunakan unittest dan from mock import Mockdengan Python Mock 0.1.0 untuk pengujian saya. Apakah ada yang terdengar bermasalah?
Nathan Arthur
Saya mengejek kelas yang dapat dipanggil dari modul lain, jadi sepertinya module_to_test.another_module.class = mock.Mock(), dapatkah Anda mengonfirmasi ini tidak mengingat panggilan di berbagai kasus pengujian (contoh unittest.TestCase)? Saya pikir jumlah panggilan tidak diatur ulang dalam kasus ini
0xc0de
66

Meskipun pertanyaan lama, saya ingin menambahkan bahwa saat ini mockperpustakaan (backport of unittest.mock) mendukung assert_not_calledmetode.

Cukup tingkatkan versi Anda;

pip install mock --upgrade

Ahmet
sumber
29

Anda dapat memeriksa calledatributnya, tetapi jika penegasan Anda gagal, hal berikutnya yang ingin Anda ketahui adalah sesuatu tentang panggilan tak terduga, sehingga Anda juga dapat mengatur agar informasi tersebut ditampilkan dari awal. Dengan menggunakan unittest, Anda dapat memeriksa konten call_args_listsebagai gantinya:

self.assertItemsEqual(my_var.call_args_list, [])

Ketika gagal, ia memberikan pesan seperti ini:

AssertionError: Jumlah elemen tidak sama:
Pertama memiliki 0, kedua memiliki 1: panggilan ('argumen pertama', 4)
Rob Kennedy
sumber
14

Ketika Anda menguji menggunakan kelas inherit unittest.TestCase Anda cukup menggunakan metode seperti:

  • menegaskanTrue
  • assertFalse
  • sama saja

dan serupa (dalam dokumentasi python Anda menemukan sisanya).

Dalam contoh Anda, kami dapat dengan mudah menyatakan jika properti mock_method.called adalah False , yang berarti metode itu tidak dipanggil.

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

Dengan python >= 3.5Anda bisa menggunakan mock_object.assert_not_called().

valex
sumber
1

Menilai dari jawaban lain, tidak ada seorang pun kecuali @ rob-kennedy yang berbicara tentang call_args_list.

Ini adalah alat yang ampuh untuk itu Anda dapat menerapkan sebaliknya MagicMock.assert_called_with()

call_args_listadalah daftar callobjek. Setiap callobjek mewakili panggilan yang dibuat pada panggilan yang diejek.

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

Mengkonsumsi callobjek itu mudah, karena Anda bisa membandingkannya dengan tuple dengan panjang 2 di mana komponen pertama adalah tuple yang berisi semua argumen posisi panggilan terkait, sedangkan komponen kedua adalah kamus argumen kata kunci.

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

Jadi, cara untuk mengatasi masalah khusus OP adalah

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

Perhatikan bahwa dengan cara ini, alih-alih hanya memeriksa apakah callable yang diejek telah dipanggil, via MagicMock.called, Anda sekarang dapat memeriksa apakah telah dipanggil dengan serangkaian argumen tertentu.

Itu berguna. Katakanlah Anda ingin menguji suatu fungsi yang mengambil daftar dan memanggil fungsi lain compute(),, untuk masing-masing nilai daftar hanya jika mereka memenuhi kondisi tertentu.

Anda sekarang dapat mengejek compute, dan menguji apakah telah dipanggil pada beberapa nilai tetapi tidak pada yang lain.

Giuseppe Crinò
sumber