Tujuh Teknik Terbukti untuk Melarikan Diri dari "Neraka Mengejek" dalam Ujian Sawa
Pengenalan
Kecewa dengan pustaka unittest.mock
Python? Adakah ujian anda masih membuat panggilan rangkaian sebenar atau membuang AttributeError
mesej yang mengelirukan? Masalah biasa ini, yang sering digelar "Neraka Mengejek," membawa kepada ujian yang perlahan, tidak boleh dipercayai dan sukar untuk dikekalkan. Siaran ini menerangkan sebab mengejek adalah penting untuk ujian yang pantas dan boleh dipercayai dan menyediakan tujuh strategi praktikal untuk menampal, mengejek dan mengasingkan kebergantungan dengan berkesan, memastikan "Kesihatan Mengejek." Teknik ini akan memperkemas aliran kerja anda dan mencipta suite ujian yang mantap, tanpa mengira pengalaman ujian Python anda.
Cabaran: Kebergantungan Luaran dalam Ujian Unit
Perisian moden kerap berinteraksi dengan sistem luaran—pangkalan data, sistem fail, API web, dll. Apabila interaksi ini meresap ke dalam ujian unit, ia menyebabkan:
AttributeError
samar atau ejekan separa.Pemaju, jurutera QA dan pengurus projek semuanya mendapat manfaat daripada ujian yang lebih bersih dan lebih dipercayai. Ujian yang gagal secara rawak atau mengakses perkhidmatan sebenar mengganggu saluran paip CI/CD dan pembangunan yang perlahan. Pengasingan berkesan kebergantungan luar adalah penting. Tetapi bagaimana kita memastikan ejekan yang betul sambil mengelakkan perangkap biasa?
Tujuh Hack untuk Mengelakkan "Ejek Neraka"
Tujuh teknik berikut menyediakan rangka kerja—senarai semak "Kesihatan Mengejek"—untuk memastikan ujian anda cekap, tepat dan pantas.
Ralat biasa ialah menampal fungsi pada definisinya, bukan di mana ia dipanggil. Python menggantikan simbol dalam modul yang sedang diuji, jadi anda mesti menampal dalam konteks import modul tersebut.
<code class="language-python"># my_module.py from some.lib import foo def do_things(): foo("hello")</code>
@patch("some.lib.foo")
@patch("my_module.foo")
Tampalan my_module.foo
memastikan penggantian di mana-mana sahaja ujian anda menggunakannya.
Anda boleh menggantikan fungsi/kelas individu atau keseluruhan modul.
<code class="language-python"># my_module.py from some.lib import foo def do_things(): foo("hello")</code>
MagicMock
. Setiap fungsi/kelas menjadi olok-olok:<code class="language-python">from unittest.mock import patch with patch("my_module.foo") as mock_foo: mock_foo.return_value = "bar"</code>
Jika kod anda memanggil atribut my_module
lain, takrifkannya pada mock_mod
atau hadapi AttributeError
.
Traceback boleh mengelirukan. Perkara utama ialah bagaimana kod anda mengimport fungsi. Sentiasa:
my_module.py
).<code class="language-python">with patch("my_module") as mock_mod: mock_mod.foo.return_value = "bar" # Define all attributes your code calls!</code>
atau
<code class="language-python">from mypackage.submodule import function_one</code>
sub.function_one()
, tampal "my_module.sub.function_one"
.from mypackage.submodule import function_one
, tampal "my_module.function_one"
.Mock out panggilan kepada sumber luaran (permintaan rangkaian, fail I/O, arahan sistem) kepada:
Contohnya, jika fungsi anda membaca fail:
<code class="language-python">import mypackage.submodule as sub</code>
Tampalkannya dalam ujian anda:
<code class="language-python">def read_config(path): with open(path, 'r') as f: return f.read()</code>
Olok-olok keseluruhan kaedah mengendalikan sumber luaran atau tampal panggilan perpustakaan individu. Pilih berdasarkan perkara yang anda sahkan.
<code class="language-python">from unittest.mock import patch @patch("builtins.open", create=True) def test_read_config(mock_open): mock_open.return_value.read.return_value = "test config" result = read_config("dummy_path") assert result == "test config"</code>
<code class="language-python">class MyClass: def do_network_call(self): pass @patch.object(MyClass, "do_network_call", return_value="mocked") def test_something(mock_call): # The real network call is never made ...</code>
Tompok peringkat tinggi lebih pantas tetapi langkau ujian kaedah dalaman. Tompok aras rendah menawarkan kawalan yang lebih halus tetapi boleh menjadi lebih kompleks.
Apabila menampal keseluruhan modul, ia menjadi MagicMock()
tanpa atribut lalai. Jika kod anda memanggil:
<code class="language-python">@patch("my_module.read_file") @patch("my_module.fetch_data_from_api") def test_something(mock_fetch, mock_read): ...</code>
Dalam ujian anda:
<code class="language-python">import my_service my_service.configure() my_service.restart()</code>
Terlupa untuk mentakrifkan atribut menghasilkan AttributeError: Mock object has no attribute 'restart'
.
Jika tindanan panggilan terlalu rumit, tampal fungsi peringkat tinggi untuk mengelakkan capaian import yang lebih mendalam. Contohnya:
<code class="language-python">with patch("path.to.my_service") as mock_service: mock_service.configure.return_value = None mock_service.restart.return_value = None ...</code>
Apabila anda tidak perlu menguji complex_operation
:
<code class="language-python">def complex_operation(): # Calls multiple external functions pass</code>
Ini mempercepatkan ujian tetapi memintas ujian complex_operation
dalaman.
Impak dan Faedah
Menggunakan strategi "Kesihatan Mengejek" ini membuahkan hasil:
AttributeError
dan isu yang serupa.Pasukan yang menggunakan amalan ini sering melihat saluran paip CI/CD yang lebih dipercayai, kurang penyahpepijatan dan pembangunan ciri yang lebih cekap.
<code class="language-python"># my_module.py from some.lib import foo def do_things(): foo("hello")</code>
Rajah ini menggambarkan cara tampalan yang betul memintas panggilan luaran, menghasilkan ujian yang lebih lancar.
Pertimbangan Masa Depan
Ejek ular sawa memang hebat. Pertimbangkan:
pytest-mock
menawarkan sintaks yang dipermudahkan.Tingkatkan suite ujian anda hari ini! Gunakan teknik ini dan kongsi hasil anda. Mari kekalkan "Kesihatan Mengejek" yang sangat baik dalam projek Python kami!
Atas ialah kandungan terperinci Hacks ratikal untuk Mengelakkan 'Ejek Neraka' dalam Ujian Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!