Saya bekerja dengan banyak aplikasi web yang digerakkan oleh basis data dengan berbagai kompleksitas di backend. Biasanya, ada lapisan ORM yang terpisah dari logika bisnis dan presentasi. Ini membuat unit-test logika bisnis cukup mudah; hal-hal dapat diimplementasikan dalam modul diskrit dan data apa pun yang diperlukan untuk pengujian dapat dipalsukan melalui objek yang mengejek.
Tetapi menguji ORM dan database itu sendiri selalu penuh dengan masalah dan kompromi.
Selama bertahun-tahun, saya telah mencoba beberapa strategi, tidak ada yang benar-benar memuaskan saya.
Memuat database uji dengan data yang diketahui. Jalankan tes terhadap ORM dan konfirmasikan bahwa data yang tepat kembali. Kerugiannya di sini adalah bahwa DB pengujian Anda harus mengikuti perubahan skema dalam database aplikasi, dan mungkin tidak sinkron. Itu juga bergantung pada data buatan, dan mungkin tidak mengekspos bug yang terjadi karena input pengguna yang bodoh. Akhirnya, jika database pengujian kecil, itu tidak akan mengungkapkan inefisiensi seperti indeks yang hilang. (Oke, yang terakhir itu sebenarnya bukan unit pengujian yang seharusnya digunakan, tetapi tidak sakit.)
Muat salinan database produksi dan uji terhadap itu. Masalahnya di sini adalah bahwa Anda mungkin tidak tahu apa yang ada di DB produksi pada waktu tertentu; tes Anda mungkin perlu ditulis ulang jika data berubah seiring waktu.
Beberapa orang telah menunjukkan bahwa kedua strategi ini bergantung pada data tertentu, dan unit test hanya menguji fungsionalitas. Untuk itu, saya telah melihat saran:
- Gunakan server database tiruan, dan periksa hanya bahwa ORM mengirim pertanyaan yang benar sebagai tanggapan terhadap panggilan metode yang diberikan.
Strategi apa yang telah Anda gunakan untuk menguji aplikasi berbasis database, jika ada? Apa yang terbaik untuk Anda?
sumber
Jawaban:
Saya sebenarnya menggunakan pendekatan pertama Anda dengan cukup sukses, tetapi dengan cara yang sedikit berbeda yang saya pikir akan menyelesaikan beberapa masalah Anda:
Simpan seluruh skema dan skrip untuk membuatnya dalam kontrol sumber sehingga siapa pun dapat membuat skema database saat ini setelah check out. Selain itu, simpan sampel data dalam file data yang bisa dimuat oleh bagian dari proses pembuatan. Saat Anda menemukan data yang menyebabkan kesalahan, tambahkan ke data sampel Anda untuk memeriksa bahwa kesalahan tidak muncul kembali.
Gunakan server integrasi berkelanjutan untuk membangun skema basis data, memuat data sampel, dan menjalankan tes. Ini adalah cara kami menjaga sinkronisasi basis data pengujian kami (membangunnya kembali di setiap percobaan). Meskipun ini mengharuskan server CI memiliki akses dan kepemilikan contoh database khusus sendiri, saya mengatakan bahwa memiliki skema db kami dibangun 3 kali sehari secara dramatis membantu menemukan kesalahan yang mungkin tidak akan ditemukan sampai sebelum pengiriman (jika tidak nanti ). Saya tidak bisa mengatakan bahwa saya membangun kembali skema sebelum setiap komitmen. Apakah ada Dengan pendekatan ini Anda tidak perlu (well mungkin kita harus, tetapi itu bukan masalah besar jika seseorang lupa).
Untuk grup saya, input pengguna dilakukan pada level aplikasi (bukan db) jadi ini diuji melalui tes unit standar.
Memuat Salinan Basis Data Produksi:
Ini adalah pendekatan yang digunakan pada pekerjaan terakhir saya. Itu adalah penyebab besar dari beberapa masalah:
Mengejek Database Server:
Kami juga melakukan ini di pekerjaan saya saat ini. Setelah setiap komit, kami menjalankan tes unit terhadap kode aplikasi yang telah disuntikkan pengakses mock db. Kemudian tiga kali sehari kita menjalankan build db lengkap yang dijelaskan di atas. Saya merekomendasikan kedua pendekatan.
sumber
Saya selalu menjalankan tes terhadap DB dalam memori (HSQLDB atau Derby) karena alasan berikut:
DB dalam memori dimuat dengan data baru setelah tes dimulai dan setelah sebagian besar tes, saya memanggil ROLLBACK agar tetap stabil. SELALU menjaga data dalam uji DB stabil! Jika data berubah sepanjang waktu, Anda tidak dapat menguji.
Data dimuat dari SQL, DB template atau dump / cadangan. Saya lebih suka kesedihan jika mereka berada dalam format yang dapat dibaca karena saya dapat menempatkannya dalam VCS. Jika itu tidak berhasil, saya menggunakan file CSV atau XML. Jika saya harus memuat data dalam jumlah besar ... Saya tidak. Anda tidak perlu memuat data dalam jumlah besar :) Tidak untuk pengujian unit. Tes kinerja adalah masalah lain dan aturan yang berbeda berlaku.
sumber
Saya telah menanyakan pertanyaan ini sejak lama, tetapi saya pikir tidak ada peluru perak untuk itu.
Apa yang saya lakukan saat ini adalah mengejek objek DAO dan menyimpan dalam memori representasi dari koleksi objek yang bagus yang mewakili kasus data yang menarik yang dapat hidup pada database.
Masalah utama yang saya lihat dengan pendekatan itu adalah bahwa Anda hanya menutupi kode yang berinteraksi dengan lapisan DAO Anda, tetapi tidak pernah menguji DAO itu sendiri, dan dalam pengalaman saya, saya melihat bahwa banyak kesalahan terjadi pada lapisan itu juga. Saya juga menyimpan beberapa unit test yang berjalan terhadap basis data (demi menggunakan TDD atau pengujian cepat secara lokal), tetapi tes tersebut tidak pernah berjalan di server integrasi berkelanjutan saya, karena kami tidak menyimpan database untuk tujuan itu dan saya berpikir tes yang berjalan di server CI harus mandiri.
Pendekatan lain yang saya temukan sangat menarik, tetapi tidak selalu bernilai karena sedikit memakan waktu, adalah membuat skema yang sama yang Anda gunakan untuk produksi pada database tertanam yang hanya berjalan dalam unit testing.
Meskipun tidak ada pertanyaan pendekatan ini meningkatkan cakupan Anda, ada beberapa kelemahan, karena Anda harus sedekat mungkin dengan ANSI SQL untuk membuatnya berfungsi baik dengan DBMS Anda saat ini dan penggantian yang disematkan.
Apa pun yang menurut Anda lebih relevan untuk kode Anda, ada beberapa proyek di luar sana yang mungkin membuatnya lebih mudah, seperti DbUnit .
sumber
Bahkan jika ada alat yang memungkinkan Anda untuk mengejek database Anda dalam satu atau lain cara (misalnya jOOQ 's
MockConnection
, yang dapat dilihat pada jawaban ini - disclaimer, saya bekerja untuk penjual jOOQ), saya akan menyarankan tidak mengejek database yang lebih besar dengan kompleks pertanyaan.Bahkan jika Anda hanya ingin menguji-ORM integrasi Anda, berhati-hatilah bahwa ORM mengeluarkan serangkaian pertanyaan yang sangat kompleks ke database Anda, yang mungkin berbeda dalam
Mengejek semua itu untuk menghasilkan data dummy yang masuk akal cukup sulit, kecuali jika Anda benar-benar membangun basis data kecil di dalam tiruan Anda, yang menginterpretasikan pernyataan SQL yang dikirimkan. Karena itu, gunakan basis data uji integrasi terkenal yang dapat Anda atur ulang dengan mudah dengan data terkenal, yang dengannya Anda dapat menjalankan tes integrasi.
sumber
Saya menggunakan yang pertama (menjalankan kode terhadap database uji). Satu-satunya masalah substantif yang saya lihat Anda angkat dengan pendekatan ini adalah kemungkinan skema keluar dari sinkronisasi, yang saya atasi dengan menyimpan nomor versi di basis data saya dan membuat semua perubahan skema melalui skrip yang menerapkan perubahan untuk setiap kenaikan versi.
Saya juga membuat semua perubahan (termasuk ke skema basis data) terhadap lingkungan pengujian saya terlebih dahulu, sehingga akhirnya menjadi sebaliknya: Setelah semua tes lulus, terapkan pembaruan skema ke host produksi. Saya juga menyimpan pasangan pengujian dan basis data aplikasi terpisah pada sistem pengembangan saya sehingga saya dapat memverifikasi di sana bahwa pemutakhiran db berfungsi dengan baik sebelum menyentuh kotak produksi yang sebenarnya.
sumber
Saya menggunakan pendekatan pertama tetapi sedikit berbeda yang memungkinkan untuk mengatasi masalah yang Anda sebutkan.
Segala sesuatu yang diperlukan untuk menjalankan tes untuk DAO ada dalam kontrol sumber. Ini termasuk skema dan skrip untuk membuat DB (buruh pelabuhan sangat baik untuk ini). Jika DB yang disematkan dapat digunakan - Saya menggunakannya untuk kecepatan.
Perbedaan penting dengan pendekatan yang dijelaskan lainnya adalah bahwa data yang diperlukan untuk pengujian tidak dimuat dari skrip SQL atau file XML. Semuanya (kecuali beberapa data kamus yang konstan secara efektif) dibuat oleh aplikasi menggunakan fungsi utilitas / kelas.
Tujuan utamanya adalah membuat data yang digunakan dengan tes
Ini pada dasarnya berarti bahwa utilitas ini memungkinkan untuk menentukan secara deklaratif hanya hal-hal penting untuk pengujian dalam tes itu sendiri dan menghilangkan hal-hal yang tidak relevan.
Untuk memberikan beberapa gagasan tentang apa artinya dalam praktik, pertimbangkan tes untuk beberapa DAO yang bekerja dengan
Comment
s toPost
s ditulis olehAuthors
. Untuk menguji operasi CRUD untuk DAO tersebut, beberapa data harus dibuat dalam DB. Tes akan terlihat seperti:Ini memiliki beberapa keunggulan dibandingkan skrip SQL atau file XML dengan data uji:
Kembalikan vs Komit
Saya merasa lebih nyaman bahwa tes melakukan ketika mereka dijalankan. Pertama, beberapa efek (misalnya
DEFERRED CONSTRAINTS
) tidak dapat diperiksa jika komit tidak pernah terjadi. Kedua, ketika tes gagal, data dapat diperiksa dalam DB karena tidak dikembalikan oleh rollback.Karena ini memiliki kelemahan sehingga pengujian dapat menghasilkan data yang rusak dan ini akan menyebabkan kegagalan dalam tes lain. Untuk mengatasinya saya mencoba mengisolasi tes. Pada contoh di atas, setiap pengujian dapat membuat yang baru
Author
dan semua entitas lain dibuat terkait dengannya sehingga tabrakan jarang terjadi. Untuk menangani invarian yang tersisa yang berpotensi rusak tetapi tidak dapat dinyatakan sebagai batasan level DB, saya menggunakan beberapa pemeriksaan terprogram untuk kondisi yang salah yang dapat dijalankan setelah setiap tes tunggal (dan mereka dijalankan dalam CI tetapi biasanya dimatikan secara lokal untuk kinerja alasan).sumber
PostBuilder.post()
. Ini menghasilkan beberapa nilai untuk semua atribut wajib posting. Ini tidak diperlukan dalam kode produksi.Untuk proyek berbasis JDBC (langsung atau tidak langsung, misalnya JPA, EJB, ...) Anda tidak dapat mengejek seluruh basis data (dalam hal demikian akan lebih baik menggunakan tes db pada RDBMS nyata), tetapi hanya mockup di tingkat JDBC .
Keuntungan adalah abstraksi yang datang dengan cara itu, karena data JDBC (set hasil, jumlah pembaruan, peringatan, ...) adalah sama, apa pun backendnya: db produk Anda, db tes, atau hanya beberapa data mockup yang disediakan untuk setiap tes kasus.
Dengan koneksi JDBC yang dipermainkan untuk setiap kasus, tidak perlu mengelola tes db (pembersihan, hanya satu tes pada waktu, memuat ulang perlengkapan, ...). Setiap koneksi mockup terisolasi dan tidak perlu dibersihkan. Hanya perlengkapan minimal yang diperlukan yang disediakan di setiap test case untuk mempermainkan pertukaran JDBC, yang membantu untuk menghindari kerumitan dalam mengelola seluruh test db.
Acolyte adalah kerangka kerja saya yang mencakup driver dan utilitas JDBC untuk jenis mockup ini: http://acolyte.eu.org .
sumber