Unit testing di Django

12

Saya benar-benar berjuang untuk menulis unit test yang efektif untuk proyek Django besar. Saya memiliki cakupan tes yang cukup baik, tetapi saya menyadari bahwa tes yang saya tulis jelas merupakan tes integrasi / penerimaan, bukan tes unit sama sekali, dan saya memiliki bagian penting dari aplikasi saya yang tidak diuji secara efektif. Saya ingin memperbaiki ASAP ini.

Inilah masalah saya. Skema saya sangat relasional, dan sangat berorientasi waktu, memberikan objek model saya kopling internal yang tinggi dan banyak keadaan. Banyak model metode saya kueri berdasarkan interval waktu, dan saya punya banyak hal auto_now_addterjadi di bidang timestamped. Jadi ambil metode yang terlihat seperti ini misalnya:

def summary(self, startTime=None, endTime=None):
    # ... logic to assign a proper start and end time 
    # if none was provided, probably using datetime.now()

    objects = self.related_model_set.manager_method.filter(...)

    return sum(object.key_method(startTime, endTime) for object in objects)

Bagaimana satu pendekatan menguji sesuatu seperti ini?

Di sinilah saya sejauh ini. Terjadi pada saya bahwa tujuan pengujian unit harus diberikan beberapa perilaku mengejek by key_methodpada argumennya, apakah summarybenar penyaringan / agregasi untuk menghasilkan hasil yang benar?

Mengolok-olok datetime.now () cukup mudah, tetapi bagaimana saya bisa mengejek perilaku lainnya?

  • Saya bisa menggunakan perlengkapan, tapi saya sudah mendengar pro dan kontra menggunakan perlengkapan untuk membangun data saya (rawatan yang buruk menjadi con yang membuat saya pulang).
  • Saya juga bisa mengatur data saya melalui ORM, tapi itu bisa membatasi, karena dengan begitu saya harus membuat objek terkait juga. Dan ORM tidak membiarkan Anda mengacaukan auto_now_addbidang secara manual.
  • Mengejek ORM adalah pilihan lain, tetapi tidak hanya sulit untuk mengejek metode ORM yang bersarang, tetapi logika dalam kode ORM akan diejek dari tes, dan mengejek tampaknya membuat tes benar-benar tergantung pada internal dan dependensi dari tes fungsi

Kacang yang paling sulit retak tampaknya adalah fungsi-fungsi seperti ini, yang terdapat pada beberapa lapisan model dan fungsi tingkat yang lebih rendah dan sangat tergantung pada waktu, walaupun fungsi-fungsi ini mungkin tidak terlalu rumit. Masalah saya secara keseluruhan adalah bahwa tidak peduli bagaimana saya mengirisnya, pengujian saya terlihat jauh lebih kompleks daripada fungsi yang mereka uji.

acjay
sumber
Anda harus menulis unit test terlebih dahulu dari sekarang yang satu ini membantu Anda menemukan masalah testabilitas dalam desain Anda sebelum kode produksi yang sebenarnya ditulis.
Chedy2149
2
Itu membantu, tetapi tidak benar-benar mengatasi masalah tentang bagaimana cara terbaik menguji aplikasi stateful, berat ORM.
acjay
Anda harus mengabstraksi lapisan persistensi
Chedy2149
1
Kedengarannya hebat secara hipotetis, tetapi ketika menyangkut pemeliharaan proyek, saya pikir ada biaya non-sepele untuk menyisipkan lapisan ketekunan yang dibuat khusus antara logika bisnis dan Django ORM yang didokumentasikan dengan sangat baik. Tiba-tiba, kelas menjadi penuh dengan sekelompok metode perantara kecil yang mereka sendiri perlu refactored dari waktu ke waktu. Tapi mungkin ini dibenarkan di tempat-tempat di mana testabilitas sangat penting.
acjay
Lihat ini: vimeo.com/43612849 dan ini: vimeo.com/15007792
Chedy2149

Jawaban:

6

Saya akan pergi ke depan dan mendaftarkan jawaban untuk apa yang saya dapatkan sejauh ini.

Hipotesis saya adalah bahwa untuk suatu fungsi dengan kopling dalam dan keadaan, kenyataannya adalah bahwa itu hanya akan mengambil banyak garis untuk mengontrol konteks luarnya.

Begini kira-kira kasus pengujian saya, dengan mengandalkan pustaka Mock standar:

  1. Saya menggunakan ORM standar untuk mengatur urutan kejadian
  2. Saya membuat awal saya sendiri datetimedan menumbangkan auto_now_addwaktu agar sesuai dengan garis waktu tetap desain saya. Saya pikir ORM tidak mengizinkan ini, tetapi berfungsi dengan baik.
  3. Saya memastikan fungsi-di-tes menggunakan from datetime import datetimesehingga saya bisa menambal datetime.now()hanya dalam fungsi itu (jika saya mengejek seluruh datetimekelas, ORM cocok).
  4. Saya membuat pengganti untuk saya sendiri object.key_method(), dengan fungsionalitas sederhana namun terdefinisi dengan baik yang bergantung pada argumen. Saya ingin ini bergantung pada argumen, karena kalau tidak saya mungkin tidak tahu apakah logika dari fungsi-di-tes berfungsi. Dalam kasus saya, itu hanya mengembalikan jumlah detik antara startTimedan endTime. Saya menambalnya dengan membungkusnya dalam lambda dan menambal langsung object.key_method()menggunakan new_callablekwarg dari patch.
  5. Akhirnya, saya menjalankan serangkaian konfirmasi pada berbagai panggilan summarydengan argumen yang berbeda untuk memeriksa kesetaraan dengan hasil yang dihitung dengan tangan yang dihitung untuk perilaku mock yang diberikan.key_method

Tak perlu dikatakan, ini jauh lebih lama dan lebih rumit daripada fungsi itu sendiri. Itu tergantung pada DB, dan tidak benar-benar terasa seperti unit test. Tetapi juga cukup dipisahkan dari internal fungsi - hanya tanda tangan dan dependensinya. Jadi saya pikir itu mungkin benar-benar menjadi unit test.

Dalam aplikasi saya, fungsi ini sangat penting, dan dapat direactoring untuk mengoptimalkan kinerjanya. Jadi saya pikir masalahnya tidak sia-sia, meskipun kompleksitas. Tapi saya masih terbuka untuk ide-ide yang lebih baik tentang cara mendekati ini. Semua bagian dari perjalanan panjang saya menuju gaya pengembangan yang lebih digerakkan oleh tes ...

acjay
sumber