Alih-alih bergabung
Unittest mungkin merupakan kerangka penulisan tes paling terkenal dengan Python. Sangat mudah dipelajari dan mudah digunakan di proyek Anda. Tapi tidak ada yang sempurna. Dalam posting ini saya ingin berbicara tentang satu fitur yang saya pribadi (saya pikir, tidak satu) kekurangan dalam unittest.
Sedikit tentang tes Unit
Sebelum membahas (dan mengutuk) kerangka kerja pengujian, saya menganggap perlu untuk berbicara sedikit tentang pengujian secara umum. Ketika saya pertama kali mendengar ungkapan "pengujian unit", saya berpikir bahwa itu adalah tanggung jawab beberapa layanan kontrol kualitas yang memeriksa modul perangkat lunak untuk kesesuaian dengan persyaratan. Bayangkan betapa terkejutnya saya ketika saya mengetahui bahwa tes yang sama ini harus ditulis oleh programmer. Awalnya saya tidak menulis tes ... sama sekali. Tidak ada yang dapat menggantikan sensasi itu ketika Anda bangun di pagi hari dan membaca dari pengguna βProgram tidak bekerja. Sesuatu perlu segera dilakukan. " Pada awalnya saya merasa bahwa ini adalah proses yang sepenuhnya normal, sampai saya menulis unit test pertama, yang menemukan kesalahan. Tes unit umumnya tampaknya tidak berguna sampai mereka mendeteksi masalah. Sekarang saya tidak bisa melakukan fungsi baru tanpa menulis tes di atasnya.
Tetapi tes tidak hanya digunakan untuk memverifikasi bahwa kode ditulis dengan benar. Berikut adalah daftar fungsi yang, menurut pendapat saya, melakukan tes:
- Deteksi kesalahan dalam kode program
- Memberikan kepercayaan kepada programmer bahwa kode mereka berfungsi
- Konsekuensi dari paragraf sebelumnya: kemampuan untuk memodifikasi program dengan aman
- Tes - semacam dokumentasi yang paling tepat menggambarkan perilaku sistem
Dalam arti tertentu, tes mengulangi struktur program. Apakah prinsip-prinsip program pembangunan dapat diterapkan pada pengujian? Saya percaya bahwa ya - ini adalah program yang sama, meskipun menguji yang lain.
Deskripsi masalah
Pada titik tertentu, saya mendapat ide untuk menulis tes abstrak. Apa yang saya maksud dengan itu? Ini adalah tes yang tidak mengeksekusi sendiri, tetapi menyatakan metode yang bergantung pada parameter yang ditentukan dalam ahli waris. Dan kemudian saya menemukan bahwa saya tidak dapat melakukan ini secara manusiawi di unittest. Berikut ini sebuah contoh:
class SerializerChecker(TestCase): model = None serializer = None def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props)
Saya pikir, bahkan tanpa mengetahui implementasi TestObjectFactory dan metode check_dict, jelas bahwa alat peraga adalah kamus properti untuk objek, obj adalah objek yang kami periksa serializer. check_dict memeriksa kamus untuk suatu pertandingan secara rekursif. Saya pikir banyak orang yang akrab dengan unittest akan segera mengatakan bahwa tes ini tidak memenuhi definisi abstrak saya. Mengapa Karena metode test_fields_creation akan mengeksekusi dari kelas ini, yang kita benar-benar tidak perlu. Setelah beberapa pencarian informasi, saya sampai pada kesimpulan bahwa opsi yang paling memadai adalah tidak mewarisi SerializerChecker dari TestCase, tetapi untuk menerapkan pewaris entah bagaimana:
class VehicleSerializerTest(SerializerChecker, RecursiveTestCase): model = Vehicle serializer = VehicleSerialize
RecursiveTestCase adalah turunan dari TestCase yang mengimplementasikan metode check_dict.
Solusi ini jelek sekaligus dari beberapa posisi:
- Di kelas SerializerChecker, kita perlu tahu bahwa anak tersebut harus mewarisi dari TestCase. Ketergantungan ini dapat menyebabkan masalah bagi mereka yang tidak terbiasa dengan kode ini.
- Lingkungan pengembangan dengan keras kepala percaya bahwa saya salah, karena SerializerChecker tidak memiliki metode check_dict
Kesalahan yang dilemparkan oleh lingkungan pengembanganTampaknya Anda dapat menambahkan rintisan untuk check_dict dan semua masalah telah diselesaikan:
class SerializerChecker: model = None serializer = None def check_dict(self, data, props): raise NotImplementedError def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props)
Tapi ini bukan solusi lengkap untuk masalah ini:
- Kami, pada kenyataannya, menciptakan antarmuka yang mengimplementasikan bukan turunan dari kelas ini, tetapi RecursiveTestCase, yang menciptakan pertanyaan wajar untuk arsitektur.
- TestCase memiliki banyak metode * tegas. Apakah kita benar-benar perlu menulis tulisan rintisan untuk setiap bekas? Masih sepertinya solusi yang bagus?
Untuk meringkas
Unittest tidak menyediakan kemampuan waras untuk "memutuskan" kelas yang diwarisi dari TestCase. Saya akan sangat senang jika fungsi seperti itu ditambahkan ke framework. Bagaimana Anda mengatasi masalah ini?