Anda benar, tes Anda seharusnya tidak memverifikasi bahwa random
modul melakukan tugasnya; unittest seharusnya hanya menguji kelas itu sendiri, bukan bagaimana ia berinteraksi dengan kode lain (yang harus diuji secara terpisah).
Tentu saja sangat mungkin kode Anda random.randint()
salah; atau Anda random.randrange(1, self._sides)
malah menelepon dan mati Anda tidak pernah melempar nilai tertinggi, tetapi itu akan menjadi jenis bug yang berbeda, bukan bug yang bisa Anda tangkap dengan yang paling sedikit. Jika demikian, die
unit Anda berfungsi seperti yang dirancang, tetapi desainnya sendiri cacat.
Dalam hal ini, saya akan menggunakan mengejek untuk menggantikan yang randint()
fungsi, dan hanya memverifikasi bahwa telah disebut dengan benar. Python 3.3 dan yang lebih baru hadir dengan unittest.mock
modul untuk menangani jenis pengujian ini, tetapi Anda dapat menginstal mock
paket eksternal pada versi yang lebih lama untuk mendapatkan fungsionalitas yang sama persis
import unittest
try:
from unittest.mock import patch
except ImportError:
# < python 3.3
from mock import patch
@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
def _make_one(self, *args, **kw):
from die import Die
return Die(*args, **kw)
def test_standard_size(self, mocked_randint):
die = self._make_one()
result = die.roll()
mocked_randint.assert_called_with(1, 6)
self.assertEqual(result, 3)
def test_custom_size(self, mocked_randint):
die = self._make_one(sides=42)
result = die.roll()
mocked_randint.assert_called_with(1, 42)
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
Dengan mengejek, tes Anda sekarang sangat sederhana; hanya ada 2 kasus, sungguh. Kasing default untuk dadu 6 sisi, dan kasing sisi khusus.
Ada cara lain untuk mengganti sementara randint()
fungsi dalam namespace global Die
, tetapi mock
modul membuatnya lebih mudah. The @mock.patch
dekorator di sini berlaku untuk semua metode pengujian dalam kasus uji; setiap metode pengujian dilewatkan argumen tambahan, random.randint()
fungsi yang diolok-olok , sehingga kita dapat menguji terhadap tiruan untuk melihat apakah itu memang telah dipanggil dengan benar. The return_value
Argumen menspesifikasikan apa yang kembali dari mock ketika itu disebut, sehingga kami dapat memverifikasi bahwa die.roll()
metode memang mengembalikan 'acak' hasil kepada kami.
Saya telah menggunakan praktik terbaik unittesting Python lain di sini: impor kelas yang sedang diuji sebagai bagian dari tes. The _make_one
Metode melakukan impor dan Instansiasi bekerja dalam tes , sehingga tes modul masih akan memuat bahkan jika Anda membuat kesalahan sintaks atau kesalahan lain yang akan mencegah modul asli untuk impor.
Dengan cara ini, jika Anda membuat kesalahan dalam kode modul itu sendiri, tes akan tetap dijalankan; mereka hanya akan gagal, memberi tahu Anda tentang kesalahan dalam kode Anda.
Agar jelas, tes di atas adalah sederhana di ekstrem. Tujuannya di sini bukan untuk menguji yang random.randint()
telah dipanggil dengan argumen yang tepat, misalnya. Sebaliknya, tujuannya adalah untuk menguji bahwa unit menghasilkan hasil yang tepat diberikan input tertentu, di mana input tersebut mencakup hasil unit lain yang tidak diuji. Dengan mengejek random.randint()
metode ini, Anda bisa mengendalikan hanya input lain ke kode Anda.
Dalam tes dunia nyata , kode aktual dalam unit-under-test Anda akan menjadi lebih kompleks; hubungan dengan input yang diteruskan ke API dan bagaimana unit lain dipanggil dapat tetap menarik, dan mengejek akan memberi Anda akses ke hasil antara, serta memungkinkan Anda menetapkan nilai balik untuk panggilan tersebut.
Misalnya, dalam kode yang mengautentikasi pengguna terhadap layanan OAuth2 pihak ketiga (interaksi multi-tahap), Anda ingin menguji bahwa kode Anda meneruskan data yang benar ke layanan pihak ke-3 itu, dan memungkinkan Anda mengejek berbagai respons kesalahan yang berbeda yang Layanan pihak ketiga akan kembali, memungkinkan Anda mensimulasikan skenario yang berbeda tanpa harus membangun sendiri server OAuth2 lengkap. Di sini penting untuk menguji bahwa informasi dari respons pertama telah ditangani dengan benar dan telah diteruskan ke panggilan tahap kedua, jadi Anda ingin melihat bahwa layanan yang dipermainkan dipanggil dengan benar.