Misalkan saya memiliki Manajer kelas yang berasal dari Karyawan kelas dasar , dan Karyawan itu memiliki metode getEmail () yang diwarisi oleh Manajer . Haruskah saya menguji bahwa perilaku metode getEmail () manajer sebenarnya sama dengan perilaku karyawan?
Pada saat tes-tes ini ditulis, tingkah lakunya akan sama, tetapi tentu saja di beberapa titik di masa depan seseorang mungkin menimpa metode ini, mengubah perilakunya, dan karena itu menghancurkan aplikasi saya. Namun tampaknya agak aneh untuk menguji pada dasarnya tidak adanya campur tangan kode.
(Perhatikan bahwa metode pengujian Manager :: getEmail () tidak meningkatkan cakupan kode (atau bahkan metrik kualitas kode lainnya (?)) Sampai Manager :: getEmail () dibuat / diganti.)
(Jika jawabannya "Ya", beberapa informasi tentang cara mengelola tes yang dibagi antara kelas dasar dan turunan akan berguna.)
Perumusan pertanyaan yang setara:
Jika kelas turunan mewarisi metode dari kelas dasar, bagaimana Anda mengekspresikan (menguji) apakah Anda mengharapkan metode yang diwarisi untuk:
- Berperilaku dengan cara yang persis sama seperti yang dilakukan basis saat ini (jika perilaku basis berubah, perilaku metode yang diturunkan tidak);
- Berperilaku persis sama dengan basis untuk semua waktu (jika perilaku kelas dasar berubah, perilaku kelas turunan juga berubah); atau
- Berperilaku namun ingin (Anda tidak peduli tentang perilaku metode ini karena Anda tidak pernah menyebutnya).
sumber
Manager
dari kelasEmployee
adalah kesalahan besar pertama.Jawaban:
Saya akan mengambil pendekatan pragmatis di sini: Jika seseorang, di masa depan, menimpa Manajer :: getMail, maka tanggung jawab pengembang untuk menyediakan kode uji untuk metode baru.
Tentu saja itu hanya valid jika
Manager::getEmail
benar-benar memiliki jalur kode yang samaEmployee::getEmail
! Bahkan jika metode ini tidak ditimpa, itu mungkin berperilaku berbeda:Employee::getEmail
bisa menelepon beberapa maya dilindungigetInternalEmail
yang sedang ditimpa diManager
.Employee::getEmail
dapat mengakses beberapa kondisi internal (mis. beberapa bidang_email
), yang dapat berbeda dalam dua implementasi: Misalnya, implementasi defaultEmployee
dapat memastikan hal_email
itu selalu[email protected]
, tetapiManager
lebih fleksibel dalam menetapkan alamat surat.Dalam kasus seperti itu, ada kemungkinan bahwa bug memanifestasikan dirinya hanya di
Manager::getEmail
, meskipun implementasi dari metode itu sendiri adalah sama. Dalam hal pengujianManager::getEmail
secara terpisah bisa masuk akal.sumber
Saya akan.
Jika Anda berpikir "baik, itu benar-benar hanya memanggil Karyawan :: getEmail () karena saya tidak menimpanya, jadi saya tidak perlu menguji Manajer :: getEmail ()" maka Anda tidak benar-benar menguji perilaku Manajer :: getEmail ().
Saya akan memikirkan apa yang hanya harus dilakukan oleh Manajer :: getEmail (), bukan apakah itu diwarisi atau diganti. Jika perilaku Manajer :: getEmail () seharusnya mengembalikan apa pun yang Karyawan :: getMail () kembali, maka itulah ujiannya. Jika perilakunya adalah mengembalikan "[email protected]", maka itulah ujiannya. Tidak masalah apakah itu diterapkan oleh warisan atau diganti.
Yang penting adalah, jika itu berubah di masa depan, tes Anda akan menangkapnya dan Anda tahu ada yang rusak atau perlu dipertimbangkan kembali.
Beberapa orang mungkin tidak setuju dengan redundansi yang tampak dalam cek, tetapi lawan saya adalah Anda menguji perilaku Karyawan :: getMail () dan Manajer :: getMail () sebagai metode yang berbeda, apakah mereka diwariskan atau tidak ditimpa. Jika pengembang di masa depan perlu mengubah perilaku Manajer :: getMail () maka mereka juga perlu memperbarui tes.
Pendapat mungkin bervariasi, saya pikir Digger dan Heinzi memberikan alasan yang masuk akal untuk hal sebaliknya.
sumber
Jangan mengujinya. Lakukan uji fungsional / penerimaan itu.
Tes unit harus menguji setiap implementasi, jika Anda tidak menyediakan implementasi baru, maka patuhi prinsip KERING. Jika Anda ingin meluangkan upaya di sini maka Anda dapat meningkatkan tes unit asli. Hanya jika Anda mengganti metode yang akan Anda menulis unit test.
Pada saat yang sama, pengujian fungsional / penerimaan harus memastikan bahwa pada akhirnya semua kode Anda melakukan apa yang seharusnya, dan semoga akan menangkap keanehan dari warisan.
sumber
Aturan Robert Martin untuk TDD adalah:
Jika Anda mengikuti aturan ini, Anda tidak akan dapat masuk ke posisi untuk mengajukan pertanyaan ini. Jika
getEmail
metode ini perlu diuji, itu akan diuji.sumber
Ya, Anda harus menguji metode yang diwariskan karena di masa depan mereka mungkin akan diganti. Juga, metode yang diwariskan dapat memanggil metode virtual itu ditimpa , yang akan mengubah perilaku metode yang diwarisi tidak ditimpa.
Cara saya menguji ini adalah dengan membuat kelas abstrak untuk menguji kelas dasar atau antarmuka (mungkin abstrak), seperti ini (dalam C # menggunakan NUnit):
Kemudian, saya memiliki kelas dengan tes khusus untuk
Manager
, dan mengintegrasikan tes karyawan seperti ini:Alasan saya bisa jadi ini adalah prinsip substitusi Liskov: tes
Employee
harus tetap lulus ketika melewatiManager
objek, karena itu berasal dariEmployee
. Jadi saya harus menulis tes saya hanya sekali, dan dapat memverifikasi mereka bekerja untuk semua kemungkinan implementasi antarmuka atau kelas dasar.sumber
setUp()
metode xUnit sendiri! Dan hampir semua kerangka kerja MVC web yang melibatkan penggantian metode "indeks" juga merusak LSP, yang pada dasarnya semuanya.class ManagerTests : ExployeeTests
? (Yaitu Mencerminkan warisan kelas yang diuji.) Apakah ini terutama estetika, untuk membantu melihat hasilnya?Saya akan mengatakan tidak karena itu akan menjadi tes berulang menurut pendapat saya, saya akan menguji sekali dalam tes Karyawan dan hanya itu.
Jika metode ini diganti maka Anda akan memerlukan tes baru untuk memeriksa perilaku yang diganti. Itu adalah tugas orang yang menerapkan
getEmail()
metode utama .sumber
Tidak, Anda tidak perlu menguji metode yang diwariskan. Kelas dan kasus uji mereka yang bergantung pada metode ini akan tetap terpecah jika perilaku berubah
Manager
.Pikirkan skenario berikut: Alamat email dirakit sebagai [email protected]:
Anda telah menguji unit ini dan berfungsi dengan baik untuk Anda
Employee
. Anda juga telah membuat kelasManager
:Sekarang Anda memiliki kelas
ManagerSort
yang mengurutkan manajer dalam daftar berdasarkan alamat email mereka. Dari Anda, Anda mengira bahwa generasi email sama dengan diEmployee
:Anda menulis ujian untuk
ManagerSort
:Semuanya bekerja dengan baik. Sekarang seseorang datang dan mengganti
getEmail()
metode:Sekarang, apa yang terjadi? Anda
testManagerSort()
akan gagal karenagetEmail()
dariManager
itu ditimpa. Anda akan menyelidiki dalam masalah ini dan akan menemukan penyebabnya. Dan semua tanpa menulis testcase terpisah untuk metode yang diwarisi.Karena itu, Anda tidak perlu menguji metode yang diwariskan.
Kalau tidak, misalnya di Jawa, Anda harus menguji semua metode yang diwarisi dari
Object
sukatoString()
,equals()
dll. Di setiap kelas.sumber