Mockを使用して関数/メソッドが呼び出されなかったことを表明する


131

Mockライブラリを使用してアプリケーションをテストしていますが、一部の関数が呼び出されなかったと主張します。モックドキュメントはやなどのメソッドについて話しますがmock.assert_called_with、モックが呼び出されmock.assert_called_once_withなかったことmock.assert_not_calledを確認するためのようなものや何かに関連するものは見つかりませんでした。

私は次のようなもので行くことができますが、それはクールでも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

これを達成する方法はありますか?


@Ahmetが彼の回答で指摘しているように、バックポート(docs.python.org/3/library/…)でもassert_not_calledがサポートされるようになりました。
マーティン

回答:


144

これはあなたのケースでうまくいくはずです。

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

サンプル;

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

この回答にはDjangoが必要ですか?エラーが発生します:AttributeError: MockCallable instance has no attribute 'called'
Nathan Arthur 14年

@NathanArthurフム、私は後に、そうは思わないsudo easy_install -U mockfrom mock import MockMacOSの、上記の実行に支障なし。Djangoをインストールしたことがない:)
Joachim Isaksson 2014年

うーん。それは変です。私はPython 2.7.1を実行しておりfrom mock import Mock、テストにunittestとPython Mock 0.1.0を使用しています。問題のある音はありますか?
Nathan Arthur

私は別のモジュールの呼び出し可能クラスをモックしているので、のように見えますがmodule_to_test.another_module.class = mock.Mock()、これが異なるテストケース(unittest.TestCaseインスタンス)全体の呼び出しを記憶していないことを確認できますか?この場合、コールカウントはリセットされないと思います
0xc0de

66

古い質問ですが、現在mockライブラリ(unittest.mockのバックポート)がサポートするassert_not_calledメソッドを追加したいと思います。

アップグレードしてください。

pip install mock --upgrade


29

called属性を確認することはできますが、アサーションが失敗した場合、次に知りたいことは予期しない呼び出しに関するものであるため、その情報を最初から表示するように調整することもできます。を使用してunittestcall_args_list代わりにの内容を確認できます。

self.assertItemsEqual(my_var.call_args_list, [])

失敗すると、次のようなメッセージが表示されます。

AssertionError:要素数が等しくありません:
最初は0、2番目は1です:call( 'first argument'、4)

14

クラスを使用してテストすると、unittest.TestCaseを継承します。次のようなメソッドを使用できます。

  • assertTrue
  • assertFalse
  • assertEqual

と同様(Pythonのドキュメントで残りを見つけます)。

あなたの例では、mock_method.calledプロパティがFalseであるかどうかを単に表明できます。これは、そのメソッドが呼び出されなかったことを意味します。

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

10

python >= 3.5使用できますmock_object.assert_not_called()


1

他の回答から判断すると、@ rob-kennedy以外の誰もについて話していませんcall_args_list

それはあなたが正確に反対を実装することができるための強力なツールです MagicMock.assert_called_with()

call_args_listcallオブジェクトのリストです。各callオブジェクトは、モックされた呼び出し可能オブジェクトに対して行われた呼び出しを表します。

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

かかるcall目的は、第2成分は、キーワード引数の辞書にあるときに、第一成分が関連するコールの全ての位置引数を含むタプル長さ2の組と比較することができるので、容易です。

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

したがって、OPの特定の問題に対処する方法は

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

この方法では、モックされた呼び出し可能オブジェクトが呼び出されたかどうかを単にチェックする代わりにMagicMock.called、特定の引数のセットで呼び出されたかどうかをチェックできることに注意してください。

それは便利です。リストを受け取る関数をテストしcompute()、特定の条件を満たす場合にのみ、リストの各値に対して別の関数を呼び出すとします。

これでモックしてcompute、他の値ではなく、ある値で呼び出されたかどうかをテストできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.