Python Mocking fungsi dari modul yang diimpor

125

Saya ingin memahami bagaimana @patchsuatu fungsi dari modul yang diimpor.

Di sinilah saya sejauh ini.

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

Ini tidak bekerja seperti yang saya harapkan. Modul "patched" hanya mengembalikan nilai unmocked dari get_user_name. Bagaimana cara meniru metode dari paket lain yang saya impor ke namespace yang sedang diuji?

nsfyn55.dll
sumber
1
Pertanyaannya adalah tentang "mengejek praktik terbaik" atau apakah yang Anda lakukan masuk akal atau tidak? Mengenai yang pertama saya akan mengatakan untuk menggunakan perpustakaan mocking seperti Mock, yang termasuk dalam python3.3 + as unittest.mock.
Bakuriu
Saya bertanya apakah saya akan melakukan hak ini. Saya melihat ke Mock, tetapi saya tidak melihat cara untuk menyelesaikan masalah khusus ini. Apakah ada cara untuk membuat ulang apa yang saya lakukan di atas di Mock?
nsfyn55

Jawaban:

167

Saat Anda menggunakan patchdekorator dari unittest.mockpaket, Anda tidak menambal namespace tempat modul diimpor (dalam hal ini app.my_module.get_user_name) Anda menambalnya di namespace yang diuji app.mocking.get_user_name.

Untuk melakukan hal di atas dengan Mockmencoba sesuatu seperti di bawah ini:

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

Dokumentasi pustaka standar menyertakan bagian berguna yang menjelaskan hal ini.

Matti John
sumber
ini masalah saya. get_user_nameberada dalam modul yang berbeda dari test_method. Apakah ada cara untuk mengejek sesuatu di sub_module? Saya memperbaikinya dengan cara yang buruk di bawah ini.
nsfyn55
6
Tidak masalah yang get_user_nameada di modul yang berbeda daripada test_methodkarena Anda mengimpor fungsi ke dalamnya app.mockingdi namespace yang sama.
Matti John
2
Dari mana test_patch berasal, sebenarnya apa itu?
Mike G
2
test_patch dilewatkan oleh penghias patch dan merupakan objek get_user_name tiruan (yaitu turunan dari kelas MagicMock). Mungkin lebih jelas jika diberi nama seperti get_user_name_patch.
Matti John
Bagaimana Anda mereferensikan test_method? Ini akan menghasilkan kesalahan, NameError: nama global 'test_method' tidak ditentukan
Aditya
12

Sementara jawaban Matti John memecahkan masalah Anda (dan membantu saya juga, terima kasih!), Namun saya akan menyarankan untuk melokalkan penggantian fungsi 'get_user_name' yang asli dengan fungsi tiruan. Ini akan memungkinkan Anda untuk mengontrol kapan fungsi diganti dan kapan tidak. Selain itu, ini memungkinkan Anda membuat beberapa penggantian dalam pengujian yang sama. Untuk melakukannya, gunakan pernyataan 'with' dengan cara yang cukup mirip:

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')
Tgilgul
sumber
6
Ini agak tidak penting untuk pertanyaan yang diajukan. Apakah Anda menggunakan patchsebagai dekorator atau pengelola konteks khusus untuk kasus penggunaan. Misalnya, Anda dapat menggunakan patchsebagai dekorator untuk mengejek nilai untuk semua pengujian di kelas xunitatau pytestsementara dalam kasus lain akan berguna untuk memiliki kontrol terperinci yang diberikan oleh pengelola konteks.
nsfyn55