Kebanyakan tutorial / contoh pengujian unit di luar sana biasanya melibatkan pendefinisian data yang akan diuji untuk setiap tes individu. Saya kira ini adalah bagian dari teori "semuanya harus diuji dalam isolasi".
Namun saya telah menemukan bahwa ketika berhadapan dengan aplikasi multitier dengan banyak DI , kode yang diperlukan untuk mengatur setiap tes menjadi sangat panjang lebar. Sebaliknya saya telah membangun sejumlah kelas testbase yang sekarang dapat saya warisi yang memiliki banyak perancah tes pra-dibangun.
Sebagai bagian dari ini, saya juga sedang membangun dataset palsu yang mewakili database aplikasi yang sedang berjalan, meskipun biasanya hanya satu atau dua baris di setiap "tabel".
Apakah ini praktik yang diterima untuk menentukan sebelumnya, jika tidak semua, maka sebagian besar data uji di semua tes unit?
Memperbarui
Dari komentar di bawah ini rasanya saya melakukan lebih banyak integrasi daripada pengujian unit.
Proyek saya saat ini adalah ASP.NET MVC, menggunakan Unit Kerangka Kerja atas Entity Code, dan Moq untuk pengujian. Saya telah mengejek UoW, dan repositori, tapi saya menggunakan kelas logika bisnis nyata, dan menguji tindakan pengontrol. Tes akan sering memeriksa bahwa UoW telah dilakukan, misalnya:
[TestClass]
public class SetupControllerTests : SetupControllerTestBase {
[TestMethod]
public void UserInvite_ExistingUser_DoesntInsertNewUser() {
// Arrange
var model = new Mandy.App.Models.Setup.UserInvite() {
Email = userData.First().Email
};
// Act
setupController.UserInvite(model);
// Assert
mockUserSet.Verify(m => m.Add(It.IsAny<UserProfile>()), Times.Never);
mockUnitOfWork.Verify(m => m.Commit(), Times.Once);
}
}
SetupControllerTestBase
adalah membangun UoW tiruan, dan instantiating userLogic
.
Banyak tes yang mengharuskan memiliki pengguna atau produk yang sudah ada dalam database, jadi saya sudah mempopulasikan apa yang dikembalikan UoW tiruan, dalam contoh ini userData
, yang hanya merupakan IList<User>
dengan satu catatan pengguna tunggal.
sumber
Jawaban:
Pada akhirnya, Anda ingin menulis kode sesedikit mungkin untuk mendapatkan hasil sebanyak mungkin. Memiliki banyak kode yang sama dalam beberapa pengujian a) cenderung menghasilkan pengkodean salin-tempel dan b) berarti bahwa jika suatu metode tanda tangan berubah, Anda akhirnya harus memperbaiki banyak tes yang rusak.
Saya menggunakan pendekatan memiliki kelas TestHelper standar yang memberi saya banyak tipe data yang saya gunakan secara rutin, jadi saya bisa membuat set entitas standar atau kelas DTO untuk pengujian saya untuk kueri dan tahu persis apa yang akan saya dapatkan setiap kali. Jadi saya bisa menelepon
TestHelper.GetFooRange( 0, 100 )
untuk mendapatkan kisaran 100 objek Foo dengan semua kelas / bidang dependensinya diatur.Khususnya di mana ada hubungan kompleks yang dikonfigurasi dalam sistem tipe ORM yang perlu hadir untuk hal-hal agar berjalan dengan benar, tetapi tidak berarti signifikan untuk pengujian ini yang dapat menghemat banyak waktu.
Dalam situasi di mana saya menguji dekat dengan tingkat data, saya kadang-kadang membuat versi uji dari kelas repositori saya yang dapat ditanyakan dengan cara yang sama (sekali lagi ini dalam lingkungan tipe ORM, dan itu tidak akan relevan terhadap suatu database nyata), karena mengejek jawaban yang tepat untuk pertanyaan banyak pekerjaan dan seringkali hanya memberikan manfaat kecil.
Ada beberapa hal yang harus diperhatikan, meskipun dalam unit test:
sumber
Apa pun yang membuat niat ujian Anda lebih mudah dibaca.
Sebagai aturan umum:
Jika data adalah bagian dari tes (mis. Tidak boleh mencetak baris dengan status 7) maka kode dalam tes, sehingga jelas apa yang dimaksudkan penulis terjadi.
Jika data hanya pengisi untuk memastikan ada sesuatu untuk dikerjakan (mis. Tidak boleh menandai catatan sebagai lengkap jika layanan pemrosesan melempar pengecualian) maka tentu saja memiliki metode BuildDummyData atau kelas uji yang menjaga data yang tidak relevan keluar dari tes .
Tetapi perhatikan bahwa saya berjuang untuk memikirkan contoh yang baik dari yang terakhir. Jika Anda memiliki banyak dari ini dalam fixture unit-test, Anda mungkin memiliki masalah yang berbeda untuk dipecahkan ... mungkin metode yang diuji terlalu kompleks.
sumber
Berbagai metode pengujian
Pertama-tama tentukan apa yang Anda lakukan: Pengujian unit atau pengujian integrasi . Jumlah lapisan tidak relevan untuk pengujian unit karena Anda hanya menguji satu kelas yang paling mungkin. Sisanya yang kau tiru. Untuk pengujian integrasi, Anda dapat menguji beberapa lapisan. Jika Anda memiliki tes unit yang baik, triknya adalah membuat tes integrasi tidak terlalu rumit.
Jika pengujian unit Anda baik, Anda tidak perlu mengulangi pengujian semua detail saat melakukan pengujian integrasi.
Istilah yang kami gunakan, itu sedikit tergantung platform, tetapi Anda dapat menemukannya di hampir semua platform pengujian / pengembangan:
Contoh aplikasi
Tergantung pada teknologi yang Anda gunakan, nama mungkin berbeda, tetapi saya akan menggunakan ini sebagai contoh:
Jika Anda memiliki aplikasi CRUD sederhana dengan Model produk, ProductsController, dan tampilan indeks yang menghasilkan tabel HTML dengan produk:
Hasil akhir dari aplikasi ini memperlihatkan tabel HTML dengan daftar semua produk yang aktif.
Pengujian unit
Model
Model yang dapat Anda uji cukup mudah. Ada berbagai metode untuk itu; kami menggunakan perlengkapan. Saya pikir itu yang Anda sebut "set data palsu". Jadi sebelum setiap tes dijalankan, kita membuat tabel, dan memasukkan data asli. Sebagian besar platform memiliki metode untuk ini. Misalnya, di kelas pengujian Anda, metode setUp () yang dijalankan sebelum setiap tes.
Kemudian kami menjalankan pengujian kami, misalnya: produk testGetAllActive .
Jadi kami menguji langsung ke database uji. Kami tidak mencemooh sumber data; kami membuatnya selalu sama. Sebagai contoh, ini memungkinkan kami untuk menguji dengan versi baru dari database, dan masalah kueri akan muncul.
Di dunia nyata Anda tidak dapat selalu mengikuti 100% tanggung jawab tunggal . Jika Anda ingin melakukan ini lebih baik, Anda bisa menggunakan sumber data yang Anda tiru. Bagi kami (kami menggunakan ORM) yang rasanya seperti menguji teknologi yang sudah ada. Juga tes menjadi jauh lebih kompleks, dan mereka tidak benar-benar menguji kueri. Jadi kita tetap seperti ini.
Data kode keras disimpan secara terpisah dalam perlengkapan. Jadi fixture-nya seperti file SQL dengan tabel buat pernyataan dan sisipan untuk catatan yang kita gunakan. Kami menjaga mereka tetap kecil kecuali ada kebutuhan nyata untuk menguji dengan banyak catatan.
Pengendali
Pengontrol membutuhkan lebih banyak pekerjaan, karena kami tidak ingin menguji model dengan itu. Jadi yang kita lakukan adalah mengejek model. Itu berarti: Kami menguji: index () metode yang harus mengembalikan daftar catatan.
Jadi kita mengejek metode model getAllActive () keluar dan menambahkan data tetap di dalamnya (dua catatan misalnya). Sekarang kami menguji data yang dikirim oleh pengontrol ke tampilan, dan kami membandingkan jika kami benar-benar mendapatkan dua catatan itu kembali.
Cukup. Kami mencoba menambahkan fungsionalitas sebagai sedikit ke controller karena itu membuat pengujian sulit. Tapi tentu saja selalu ada beberapa kode di dalamnya. Misalnya, kami menguji persyaratan seperti: Tampilkan dua catatan itu hanya jika Anda masuk.
Jadi, pengontrol membutuhkan satu tiruan secara normal dan sepotong kecil data yang dikodekan. Untuk sistem login mungkin yang lain. Dalam pengujian kami, kami memiliki metode pembantu untuk itu: setLoggedIn (). Itu membuatnya mudah untuk menguji dengan login atau tanpa login.
Tampilan
Pengujian tampilan sulit. Pertama kita memisahkan logika yang berulang. Kami memasukkannya ke dalam Pembantu dan menguji kelas-kelas itu dengan ketat. Kami mengharapkan output yang sama selalu. Misalnya, generateHtmlTableFromArray ().
Kemudian kami memiliki beberapa pandangan spesifik proyek. Kami tidak menguji itu. Tidak benar-benar diinginkan untuk menguji unit tersebut. Kami menyimpannya untuk pengujian integrasi. Karena kami mengambil banyak kode untuk dilihat, kami memiliki risiko yang lebih rendah di sini.
Jika Anda mulai menguji yang kemungkinan Anda perlu mengubah tes Anda setiap kali Anda mengubah sepotong HTML yang tidak berguna untuk sebagian besar proyek.
Tes integrasi
Bergantung pada platform Anda di sini, Anda dapat bekerja dengan cerita pengguna, dll. Ini bisa berbasis web seperti Selenium atau solusi serupa lainnya.
Secara umum kami hanya memuat basis data dengan perlengkapan dan menegaskan data mana yang harus tersedia. Untuk pengujian integrasi penuh, kami biasanya menggunakan persyaratan yang sangat global. Jadi: Atur produk ke aktif lalu periksa apakah produk tersedia.
Kami tidak menguji semuanya lagi, seperti apakah bidang yang tepat tersedia. Kami menguji persyaratan yang lebih besar di sini. Karena kami tidak ingin menduplikasi pengujian kami dari pengontrol atau tampilan. Jika ada sesuatu yang benar-benar kunci / inti dari aplikasi Anda atau untuk alasan keamanan (periksa kata sandi TIDAK tersedia) maka kami menambahkannya untuk memastikan itu benar.
Data kode keras disimpan di dalam fixture.
sumber
Jika Anda menulis tes yang melibatkan banyak DI dan perkabelan, hingga menggunakan sumber data "nyata", Anda mungkin meninggalkan area pengujian unit biasa dan memasuki domain pengujian integrasi.
Untuk tes integrasi, saya pikir, bukan ide yang buruk untuk memiliki logika pengaturan data yang umum. Tujuan utama dari tes tersebut adalah untuk membuktikan bahwa semuanya sudah terkonfigurasi dengan benar. Ini agak independen dari data konkret yang dikirim melalui sistem Anda.
Di sisi lain, untuk tes unit, saya akan merekomendasikan agar target kelas tes menjadi kelas "nyata" dan mengolok-olok semua yang lain. Maka Anda harus benar-benar meng-kode data uji untuk memastikan Anda menutupi sebanyak mungkin jalur khusus / bug sebelumnya.
Untuk menambahkan elemen semi-hard-kode / acak ke dalam tes, saya ingin memperkenalkan pabrik model acak. Dalam tes menggunakan instance dari model saya, saya kemudian menggunakan pabrik-pabrik ini untuk membuat objek model yang valid, tetapi benar-benar acak dan kemudian hard-code hanya properti yang menarik untuk pengujian yang ada. Dengan cara ini Anda menentukan semua data yang relevan secara langsung dalam pengujian Anda, sambil menghemat Anda juga perlu menentukan semua data yang tidak relevan dan (sampai tingkat tertentu) menguji bahwa tidak ada dependensi yang tidak diinginkan pada bidang model lain.
sumber
Saya pikir itu cukup umum untuk kode hard sebagian besar data untuk tes Anda.
Pertimbangkan situasi sederhana di mana kumpulan data tertentu menyebabkan bug terjadi. Anda mungkin secara khusus membuat unit test untuk data tersebut untuk melakukan perbaikan dan memastikan bahwa bug tidak kembali. Seiring waktu tes Anda akan memiliki satu set data yang mencakup sejumlah kasus uji.
Data uji yang ditentukan sebelumnya juga memungkinkan Anda untuk membangun satu set data yang mencakup berbagai situasi yang luas dan diketahui.
Yang mengatakan, saya pikir ada nilai juga dalam memiliki beberapa data acak dalam tes Anda.
sumber