Pythonインポートされたモジュールから関数をモックする


125

@patchインポートしたモジュールから関数を作成する方法を知りたい。

これが今のところです。

app / mocking.py:

from app.my_module import get_user_name

def test_method():
  return get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()

app / my_module / __ init__.py:

def get_user_name():
  return "Unmocked User"

test / mock-test.py:

import unittest
from app.mocking import test_method 

def mock_get_user():
  return "Mocked This Silly"

@patch('app.my_module.get_user_name')
class MockingTestTestCase(unittest.TestCase):

  def test_mock_stubs(self, mock_method):
    mock_method.return_value = 'Mocked This Silly')
    ret = test_method()
    self.assertEqual(ret, 'Mocked This Silly')

if __name__ == '__main__':
  unittest.main()

これは私が期待するようには機能しませ。「パッチされた」モジュールは、モックされていないの値を返すだけですget_user_name。テスト中の名前空間にインポートしている他のパッケージのメソッドをモックするにはどうすればよいですか?


1
問題は、「模倣のベストプラクティス」、またはあなたがしていることが理にかなっているかどうかです。最初のものについてはMock、Python3.3 +にとして含まれているなどのモッキングライブラリを使用すると思いますunittest.mock
Bakuriu 2013

私はこの権利について行くかどうか尋ねています。私はモックを見ましたが、この特定の問題を解決する方法がわかりません。上記でモックで行ったことを再現する方法はありますか?
nsfyn55 2013

回答:


167

パッケージpatchからデコレータを使用しているunittest.mock場合は、モジュールのインポート元の名前空間にパッチを適用しません(この場合app.my_module.get_user_nameapp.mocking.get_user_name。テスト中の名前空間にパッチを適用します。

上記を行うにはMock、次のようなことを試してください:

from mock import patch
from app.mocking import test_method 

class MockingTestTestCase(unittest.TestCase):

    @patch('app.mocking.get_user_name')
    def test_mock_stubs(self, test_patch):
        test_patch.return_value = 'Mocked This Silly'
        ret = test_method()
        self.assertEqual(ret, 'Mocked This Silly')

標準ライブラリのドキュメントには、これを説明する便利なセクションが含まれています。


これは私の問題になります。get_user_nameはとは異なるモジュールにありtest_methodます。sub_moduleで何かをモックする方法はありますか?以下の醜い方法で修正しました。
nsfyn55 2013

6
関数を同じ名前空間にインポートしているのとはget_user_name別のモジュールであってもかまいません。test_methodapp.mocking
Matti John

2
test_patchはどこから来たのですか?正確には何ですか?
Mike G

2
test_patchは、パッチデコレータによって渡され、モックされたget_user_nameオブジェクト(つまり、MagicMockクラスのインスタンス)です。のような名前を付けた方がわかりやすいかもしれませんget_user_name_patch
Matti John

test_methodをどのように参照していますか?NameError:グローバル名 'test_method'が定義されていません
Aditya

12

Matti Johnの答えはあなたの問題を解決します(そして私も助けてくれてありがとう!)、しかし、私は元の 'get_user_name'関数の置き換えを偽の関数にローカライズすることをお勧めします。これにより、関数をいつ置き換えるかを制御できます。また、これにより、同じテストでいくつかの置換を行うことができます。そのためには、 'with'ステートメントをかなり似た方法で使用します。

from mock import patch

class MockingTestTestCase(unittest.TestCase):

    def test_mock_stubs(self):
        with patch('app.mocking.get_user_name', return_value = 'Mocked This Silly'):
            ret = test_method()
            self.assertEqual(ret, 'Mocked This Silly')

6
これは、提起された質問には重要ではありません。patchデコレーターとして使用するか、コンテキストマネージャーとして使用するかは、ユースケースによって異なります。たとえばpatch、デコレータとして使用して、xunitまたはpytestクラス内のすべてのテストの値を模擬できますが、他の場合では、コンテキストマネージャが提供するきめの細かい制御が便利です。
nsfyn55 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.