Haruskah unit test ditulis untuk pengambil dan setter?

141

Apakah kita seharusnya menulis tes untuk getter dan setter kita atau apakah itu berlebihan?

Hector421
sumber
Saya kira tidak. Anda seharusnya tidak menulis test case untuk pengambil / penyetel.
Harry Joy
1
Saya menganggap Anda maksud Jawa? Ini adalah pertanyaan yang sangat akut untuk Jawa, apalagi untuk bahasa yang lebih modern.
skaffman
@skaffman Bahasa modern apa yang tidak memiliki properti? Tentu saja, bahasa seperti Java mengharuskan mereka untuk menjadi metode penuh, tetapi itu tidak membuatnya berbeda secara logis dengan mengatakan C #.
Claus Jørgensen
2
@Claus: Dia tidak mengatakan properti, katanya getter dan setter. Di java Anda menulisnya secara manual, dalam bahasa lain Anda mendapatkan dukungan yang lebih baik.
skaffman
2
Orang mungkin bertanya mengapa memiliki getter dan setter sama sekali .
Raedwald

Jawaban:

180

Saya akan mengatakan tidak.

@ Akan mengatakan Anda harus bertujuan untuk cakupan kode 100%, tetapi menurut saya itu adalah gangguan yang berbahaya. Anda dapat menulis unit test yang memiliki cakupan 100%, namun tidak menguji apa pun.

Tes unit ada untuk menguji perilaku kode Anda, dengan cara yang ekspresif dan bermakna, dan getter / setter hanya sarana untuk mencapai tujuan. Jika Anda menguji menggunakan getter / setter untuk mencapai tujuan mereka menguji fungsionalitas "nyata", maka itu cukup baik.

Jika, di sisi lain, getter dan setter Anda melakukan lebih dari sekedar mendapatkan dan mengatur (yaitu mereka metode yang benar-benar rumit), maka ya, mereka harus diuji. Tapi jangan menulis unit test case hanya untuk menguji rajin atau rajin, itu buang-buang waktu.

skaffman
sumber
7
Membidik cakupan kode 100% konyol. Anda pada akhirnya akan melakukan peliputan kode yang tidak terbuka untuk umum, dan / atau kode tanpa kerumitan. Hal-hal seperti itu sebaiknya dibiarkan autogenerasi, tetapi bahkan tes autogenerasi tidak ada gunanya. Lebih baik mengalihkan fokus ke pengujian penting, seperti tes integrasi.
Claus Jørgensen
7
@Claus: Saya setuju kecuali untuk sedikit tentang fokus pada pengujian integrasi. Tes unit sangat penting untuk desain yang baik, dan melengkapi tes integrasi.
skaffman
5
Saya pikir cakupan kode 100% ketika seluruh test suite dijalankan adalah tujuan yang baik. Setter getter properti dan kode lainnya dijalankan sebagai bagian dari pengujian yang lebih besar. Potongan terakhir yang akan dibahas mungkin penanganan kesalahan. Dan jika penanganan kesalahan tidak dicakup oleh unit test tidak akan pernah dibahas. Apakah Anda benar-benar ingin mengirimkan produk yang berisi kode yang belum pernah dijalankan?
Anders Abel
Saya cenderung tidak setuju dengan saran ini. Ya, getter dan setter sederhana, tetapi mereka juga sangat mudah untuk dikacaukan. Beberapa baris tes sederhana dan Anda tahu mereka sedang bekerja dan terus bekerja.
Charles
Anda mungkin menguji setter yang tidak perlu yang Anda (atau alat) ciptakan hanya untuk memiliki POJO 'bulat'. Anda mungkin menggunakan Gson.fromJson untuk "mengembang" POJOS (tidak diperlukan setter). Dalam hal ini pilihan saya adalah menghapus setter yang tidak digunakan.
Alberto Gaona
36

Roy Osherove dalam bukunya yang terkenal 'The Art Of Unit Testing' mengatakan:

Properti (getter / setter di Jawa) adalah contoh kode yang baik yang biasanya tidak mengandung logika apa pun, dan tidak memerlukan pengujian. Tapi hati-hati: setelah Anda menambahkan cek di dalam properti, Anda ingin memastikan bahwa logika sedang diuji.

azhidkov
sumber
1
Mengingat betapa sedikit waktu yang dibutuhkan untuk menguji itu dan kemungkinan perilaku bertambah, saya tidak mengerti mengapa tidak. Saya menduga jika ditekan dia mungkin menjawab "baik, saya kira mengapa tidak".
Kereta luncur
1
Buku-buku awal yang penting sering kali memiliki saran yang buruk. Saya telah melihat begitu banyak kasus di mana orang melemparkan getter dan setter dengan cepat dan membuat kesalahan karena mereka memotong dan menempel atau melupakan ini. atau ini-> atau sesuatu seperti itu. Mereka mudah untuk diuji dan tentunya harus diuji.
Charles
35

YA tegas dengan TDD


Catatan : Jawaban ini terus bertambah, meskipun berpotensi saran yang buruk. Untuk memahami alasannya, lihat adik perempuannya di bawah ini.


Baik kontroversial, tapi saya berpendapat bahwa siapa pun yang menjawab 'tidak' untuk pertanyaan ini kehilangan konsep dasar TDD.

Bagi saya, jawabannya adalah ya jika Anda mengikuti TDD. Jika Anda tidak maka tidak ada jawaban yang masuk akal.

DDD dalam TDD

TDD sering dikutip memiliki manfaat utama bagimu.

  • Pertahanan
    • Memastikan kode dapat berubah tetapi tidak perilakunya .
    • Ini memungkinkan praktik refactoring yang sangat penting .
    • Anda mendapatkan TDD ini atau tidak.
  • Rancangan
    • Anda menentukan apa yang harus dilakukan sesuatu, bagaimana perilakunya sebelum menerapkannya .
    • Ini sering berarti keputusan implementasi yang lebih terinformasi .
  • Dokumentasi
    • Test suite harus berfungsi sebagai dokumentasi spesifikasi (persyaratan).
    • Menggunakan tes untuk tujuan seperti itu berarti bahwa dokumentasi dan implementasi selalu dalam keadaan konsisten - perubahan ke satu berarti perubahan ke yang lain. Bandingkan dengan menjaga persyaratan dan desain pada dokumen kata yang terpisah.

Pisahkan tanggung jawab dari implementasi

Sebagai pemrogram, sangat menggoda untuk menganggap atribut sebagai sesuatu yang penting dan getter dan setter sebagai semacam overhead.

Tetapi atribut adalah detail implementasi, sementara setter dan getter adalah antarmuka kontraktual yang benar-benar membuat program bekerja.

Jauh lebih penting untuk mengeja bahwa objek harus:

Izinkan kliennya mengubah kondisinya

dan

Izinkan kliennya menanyakan kuasanya

lalu bagaimana keadaan ini sebenarnya disimpan (yang atributnya paling umum, tetapi bukan satu-satunya cara).

Tes seperti

(The Painter class) should store the provided colour

Penting untuk bagian dokumentasi TDD.

Fakta bahwa implementasi akhirnya adalah sepele (atribut) dan tidak membawa manfaat pertahanan seharusnya tidak diketahui oleh Anda ketika Anda menulis tes.

Kurangnya rekayasa bolak-balik ...

Salah satu masalah utama dalam dunia pengembangan sistem adalah tidak adanya round-trip engineering 1 - proses pengembangan sistem terpecah menjadi sub-proses yang terpotong-potong, yang artefaknya (dokumentasi, kode) sering tidak konsisten.

1 Brodie, Michael L. "John Mylopoulos: menjahit benih pemodelan konseptual." Pemodelan Konseptual: Yayasan dan Aplikasi. Springer Berlin Heidelberg, 2009. 1-9.

... dan bagaimana TDD memecahkannya

Ini adalah bagian dokumentasi TDD yang memastikan bahwa spesifikasi sistem dan kodenya selalu konsisten.

Desain dulu, implementasikan nanti

Dalam TDD kami menulis tes penerimaan gagal pertama, baru kemudian menulis kode yang membiarkan mereka lulus.

Di dalam BDD level yang lebih tinggi, kita menulis skenario terlebih dahulu, lalu membuatnya lewat.

Mengapa Anda harus mengecualikan setter dan pengambil?

Secara teori, sangat mungkin dalam TDD untuk satu orang untuk menulis tes, dan yang lain untuk mengimplementasikan kode yang membuatnya lulus.

Jadi tanyakan pada diri sendiri:

Haruskah orang yang menulis tes untuk kelas menyebutkan getter dan setter.

Karena getter dan setter adalah antarmuka publik ke kelas, jawabannya jelas ya , atau tidak akan ada cara untuk mengatur atau menanyakan keadaan suatu objek. Namun , cara untuk melakukan ini belum tentu dengan menguji setiap metode secara terpisah, lihat jawaban saya yang lain untuk lebih lanjut.

Jelas, jika Anda menulis kode terlebih dahulu, jawabannya mungkin tidak begitu jelas.

Izhaki
sumber
2
Itu hanya satu sisi dari koin. Pikirkan hal-hal yang bisa Anda lakukan alih-alih pengujian hanya demi pengujian. Seperti yang dikatakan Kent Beck, Anda dibayar untuk kode kerja, bukan untuk tes kerja.
Georgii Oleinikov
@GeorgiiOleinikov Sudahkah Anda membaca jawaban saya yang lain di bawah ini? Cukup banyak sesuai dengan pandangan Anda.
Izhaki
21

tl; dr: Ya Anda harus , dan dengan OpenPojo itu sepele.

  1. Anda harus melakukan validasi pada getter dan setter Anda sehingga Anda harus mengujinya. Misalnya, setMom(Person p)tidak boleh membiarkan siapa pun yang lebih muda dari diri mereka sebagai ibu mereka.

  2. Bahkan jika Anda tidak melakukan semua itu sekarang, kemungkinan Anda akan di masa depan, maka ini akan baik untuk analisis regresi. Jika Anda ingin mengizinkan ibu yang nullmemiliki Anda harus melakukan tes untuk itu jika seseorang mengubah itu nanti, ini akan memperkuat asumsi Anda.

  3. Bug umum adalah di void setFoo( Object foo ){ foo = foo; }mana seharusnya void setFoo( Object foo ){ this.foo = foo; }. (Dalam kasus pertama fooyang sedang ditulis untuk adalah parameter tidak dalam foolapangan pada objek ).

  4. Jika Anda mengembalikan array atau koleksi, Anda harus menguji apakah pengambil akan melakukan salinan defensif dari data yang diteruskan ke setter sebelum kembali.

  5. Kalau tidak, jika Anda memiliki setter / getter paling dasar maka unit-test mereka akan menambahkan mungkin sekitar 10 menit paling banyak per-objek, jadi apa kerugiannya? Jika Anda menambahkan perilaku Anda sudah memiliki tes kerangka dan Anda mendapatkan pengujian regresi ini secara gratis. Jika Anda menggunakan Java, Anda tidak punya alasan karena ada OpenPojo . Ada seperangkat aturan yang dapat Anda aktifkan dan kemudian memindai seluruh proyek Anda dengan mereka untuk memastikan mereka diterapkan secara konsisten dalam kode Anda.

Dari contoh mereka :

final PojoValidator pojoValidator = new PojoValidator();

//create rules
pojoValidator.addRule( new NoPublicFieldsRule  () );
pojoValidator.addRule( new NoPrimitivesRule    () );
pojoValidator.addRule( new GetterMustExistRule () );
pojoValidator.addRule( new SetterMustExistRule () );

//create testers
pojoValidator.addTester( new DefaultValuesNullTester () );
pojoValidator.addTester( new SetterTester            () );
pojoValidator.addTester( new GetterTester            () );

//test all the classes
for(  PojoClass  pojoClass :  PojoClassFactory.getPojoClasses( "net.initech.app", new FilterPackageInfo() )  )
    pojoValidator.runValidation( pojoClass );
Kereta luncur
sumber
11
Saya tahu ini sudah tua sekarang, tetapi: HARUSkah Anda melakukan validasi pada getter dan setter Anda? Saya mendapat kesan bahwa setMom harus melakukan apa yang dikatakannya. Jika itu memvalidasi, maka bukankah itu harus validateAndSetMom? Atau lebih baik, bukankah kode validasi ada di tempat lain daripada objek sederhana? Apa yang kulewatkan di sini?
ndtreviv
14
Ya, Anda harus selalu memvalidasi input Anda. Jika tidak, mengapa tidak menggunakan variabel publik saja? Itu menjadi hal yang sama. Seluruh manfaat menggunakan setter versus variabel adalah memungkinkan Anda untuk memastikan objek Anda tidak pernah tidak valid, seperti usia menjadi angka negatif. Jika Anda tidak melakukan ini di objek Anda akan melakukannya di tempat lain (seperti objek "layanan" dewa ) dan itu tidak benar-benar OOP pada saat itu.
Kereta luncur
1
Yah, itu mungkin yang paling penting dari minggu saya. Terima kasih.
Kereta luncur
19

Ya, tetapi tidak selalu terpisah

Izinkan saya menguraikan:

Apa itu unit test?

Dari Bekerja secara efektif dengan kode lawas 1 :

Istilah unit test memiliki sejarah panjang dalam pengembangan perangkat lunak. Umum untuk sebagian besar konsepsi unit test adalah gagasan bahwa mereka adalah tes dalam isolasi masing-masing komponen perangkat lunak. Apa itu komponen? Definisi bervariasi, tetapi dalam pengujian unit, kami biasanya berkaitan dengan unit perilaku paling atom dari suatu sistem. Dalam kode prosedural, unit sering berfungsi. Dalam kode berorientasi objek, unit adalah kelas.

Perhatikan bahwa dengan OOP, di mana Anda menemukan getter dan setter, unit adalah kelas , tidak harus metode individual .

Apa tes yang bagus?

Semua persyaratan dan tes mengikuti bentuk logika Hoare :

{P} C {Q}

Dimana:

  • {P}adalah prasyarat ( diberikan )
  • Cadalah kondisi pemicu ( kapan )
  • {Q}adalah kondisi akhir ( lalu )

Kemudian muncul pepatah:

Uji perilaku, bukan implementasi

Ini berarti bahwa Anda tidak boleh menguji bagaimana Cmencapai kondisi pasca, Anda harus memeriksa {Q}apakah hasilnya C.

Ketika datang ke OOP, Cadalah kelas. Jadi Anda tidak harus menguji efek internal, hanya efek eksternal.

Mengapa tidak menguji pembuat dan penyetel kacang secara terpisah

Getters dan setters mungkin melibatkan beberapa logika, tetapi selama logika ini tidak memiliki efek eksternal - membuat mereka bean accessors 2 ) tes harus melihat ke dalam objek dan dengan itu tidak hanya melanggar enkapsulasi tetapi juga tes untuk implementasi.

Jadi, Anda tidak harus menguji pembuat dan pembuat bean secara terpisah. Ini buruk:

Describe 'LineItem class'

    Describe 'setVAT()'

        it 'should store the VAT rate'

            lineItem = new LineItem()
            lineItem.setVAT( 0.5 )
            expect( lineItem.vat ).toBe( 0.5 )

Meskipun jika setVATakan melempar pengecualian, tes yang sesuai akan tepat karena sekarang ada efek eksternal.

Bagaimana seharusnya Anda menguji getter dan setter?

Hampir tidak ada gunanya mengubah keadaan internal suatu objek jika perubahan tersebut tidak memiliki efek di luar, bahkan jika efek tersebut datang kemudian.

Jadi tes untuk setter dan getter harus memperhatikan efek eksternal dari metode ini, bukan yang internal.

Sebagai contoh:

Describe 'LineItem class'

    Describe 'getGross()'

        it 'should return the net time the VAT'

            lineItem = new LineItem()
            lineItem.setNet( 100 )
            lineItem.setVAT( 0.5 )
            expect( lineItem.getGross() ).toBe( 150 )

Anda mungkin berpikir sendiri:

Tunggu sebentar, kami sedang menguji di getGross()sini bukan setVAT() .

Tetapi jika setVAT()kerusakan itu tes harus gagal semua sama.

1 Feathers, M., 2004. Bekerja secara efektif dengan kode warisan. Prentice Hall Professional.

2 Martin, RC, 2009. Clean code: buku pegangan keahlian pengerjaan perangkat lunak tangkas. Pendidikan Pearson.

Izhaki
sumber
13

Sementara ada alasan yang dibenarkan untuk Properti, ada kepercayaan Desain Berorientasi Objek umum bahwa mengekspos negara anggota melalui Properti adalah desain yang buruk. Artikel Robert Martin tentang Prinsip Tertutup Terbuka memperluas ini dengan menyatakan bahwa Properti mendorong penggandaan dan karenanya membatasi kemampuan untuk menutup kelas dari modifikasi - jika Anda memodifikasi properti, semua konsumen kelas perlu mengubah juga. Dia memenuhi syarat bahwa mengekspos variabel anggota tidak harus desain yang buruk, mungkin hanya gaya yang buruk. Namun, jika properti hanya baca, ada sedikit peluang penyalahgunaan dan efek samping.

Pendekatan terbaik yang dapat saya berikan untuk pengujian unit (dan ini mungkin aneh) adalah membuat sebanyak mungkin properti terlindungi atau internal. Ini akan mencegah penggandengan saat mengecilkan menulis tes konyol untuk getter dan setter.

Ada alasan yang jelas di mana Properti baca / tulis harus digunakan, seperti properti ViewModel yang terikat pada bidang input, dll.

Secara lebih praktis, tes unit harus mendorong fungsionalitas melalui metode publik. Jika kode yang Anda uji menggunakan properti itu, Anda mendapatkan cakupan kode secara gratis. Jika ternyata properti ini tidak pernah disorot oleh cakupan kode, ada kemungkinan yang sangat kuat bahwa:

  1. Anda melewatkan tes yang secara tidak langsung menggunakan properti
  2. Properti tidak digunakan

Jika Anda menulis tes untuk getter dan setter, Anda mendapatkan sense of coverage yang salah dan tidak akan dapat menentukan apakah properti benar-benar digunakan oleh perilaku fungsional.

bryanbcook
sumber
12

Jika kompleksitas siklis dari pengambil dan / atau penyetel adalah 1 (yang biasanya), maka jawabannya tidak, Anda tidak harus.

Jadi kecuali Anda memiliki SLA yang membutuhkan 100% cakupan kode, jangan repot-repot, dan fokus pada pengujian aspek penting dari perangkat lunak Anda.

PS Ingatlah untuk membedakan getter dan setter, bahkan dalam bahasa seperti C # di mana properti mungkin tampak seperti hal yang sama. Kompleksitas setter bisa lebih tinggi daripada pengambil, dan dengan demikian memvalidasi tes-unit.

Claus Jørgensen
sumber
4
Meskipun seseorang harus menguji untuk menyalin defensif pada getter
Sled
8

Pandangan lucu, namun bijak: Jalan Testivus

"Tulis tes yang kamu bisa hari ini"

Pengujian getter / setter mungkin berlebihan jika Anda seorang penguji yang berpengalaman dan ini adalah proyek kecil. Namun, jika Anda baru saja mulai belajar cara menguji unit atau getter / setter ini mungkin mengandung logika (seperti setMom()contoh @ ArtB ) maka akan lebih baik untuk menulis tes.

muymoo
sumber
1
tautan Anda seharusnya menunjuk ke: Jalan Testivus
JJS
2

Ini sebenarnya telah menjadi topik terbaru antara tim saya dan saya. Kami mengambil cakupan kode 80%. Tim saya berpendapat bahwa getter dan setter otomatis diimplementasikan, dan kompiler menghasilkan beberapa kode dasar di belakang layar. Dalam hal ini, mengingat kode yang dihasilkan tidak mengganggu, tidak masuk akal untuk menguji kode yang dibuat oleh kompiler untuk Anda. Kami juga memiliki diskusi tentang metode async dan dalam hal ini kompiler menghasilkan sejumlah kode di belakang layar. Ini adalah kasus yang berbeda dan sesuatu yang kami uji. Jawaban panjang pendek, bawa bersama tim Anda dan putuskan sendiri apakah layak untuk diuji.

Juga, jika Anda menggunakan laporan cakupan kode seperti kami, satu hal yang dapat Anda lakukan adalah menambahkan atribut [ExcludeFromCodeCoverage]. Solusi kami adalah menggunakan ini untuk model yang hanya memiliki properti menggunakan getter dan setter, atau di properti itu sendiri. Dengan begitu hal itu tidak akan mempengaruhi% cakupan kode total ketika laporan cakupan kode dijalankan, dengan asumsi itulah yang Anda gunakan untuk menghitung persentase cakupan kode Anda. Selamat mencoba!

Devin C
sumber
2

Dalam cakupan kode pendapat saya adalah cara yang baik untuk melihat apakah Anda melewatkan fungsi yang harus Anda liput.

Ketika Anda memeriksa cakupan secara manual dengan pewarnaan yang cantik maka dapat diperdebatkan bahwa getter dan setter biasa tidak perlu diuji (meskipun saya selalu melakukannya).

Ketika Anda hanya memeriksa persentase cakupan kode pada proyek Anda, maka persentase cakupan tes seperti 80% tidak ada artinya. Anda dapat menguji semua bagian yang tidak logis dan melupakan beberapa bagian penting. Dalam hal ini hanya 100% berarti bahwa Anda telah menguji semua kode vital Anda (dan semua kode non-logis juga). Begitu 99,9% Anda tahu bahwa Anda telah melupakan sesuatu.

Omong-omong: Cakupan kode adalah pemeriksaan terakhir untuk melihat apakah Anda telah (sepenuhnya) menguji suatu kelas. Tetapi cakupan kode 100% tidak selalu berarti bahwa Anda telah benar-benar menguji semua fungsionalitas kelas. Jadi unit test harus selalu dilaksanakan mengikuti logika kelas. Pada akhirnya Anda menjalankan cakupan untuk melihat apakah Anda lupa sesuatu. Ketika Anda melakukannya dengan benar, Anda menekan 100% pertama kalinya.

Satu hal lagi: Ketika baru-baru ini bekerja di sebuah bank besar di Belanda saya perhatikan bahwa Sonar menunjukkan cakupan kode 100%. Namun, saya tahu ada sesuatu yang hilang. Memeriksa persentase cakupan kode per file, ini menunjukkan file dengan persentase lebih rendah. Seluruh persentase basis kode adalah sebesar itu sehingga satu file tidak membuat persentase ditampilkan sebagai 99,9%. Jadi, Anda mungkin ingin melihat keluar untuk ini ...

Radboud
sumber
1

Saya melakukan sedikit analisis tentang cakupan yang dicapai dalam kode JUnit itu sendiri .

Salah satu kategori kode terbuka adalah "terlalu mudah untuk diuji" . Ini termasuk getter dan setter sederhana, yang tidak diuji oleh pengembang JUnit .

Di sisi lain, JUnit tidak memiliki metode (non-usang) lebih dari 3 baris yang tidak dicakup oleh tes apa pun.

avandeursen
sumber
1

Saya akan mengatakan: YA Kesalahan dalam metode pengambil / penyetel dapat diam-diam menyelinap masuk dan menyebabkan beberapa bug jelek.

Saya telah menulis lib untuk membuat ini dan beberapa tes lainnya lebih mudah. Satu-satunya hal yang harus Anda tulis dalam tes JUnit Anda adalah ini:

        assertTrue(executor.execute(Example.class, Arrays.asList( new DefensiveCopyingCheck(),
            new EmptyCollectionCheck(), new GetterIsSetterCheck(),
            new HashcodeAndEqualsCheck(), new PublicVariableCheck())));

-> https://github.com/Mixermachine/base-test

Aaron Dietz
sumber
0

Ya, terutama jika item yang didapat adalah objek kelas yang disubclass dari kelas abstrak. IDE Anda mungkin atau mungkin tidak mengingatkan Anda bahwa properti tertentu belum diinisialisasi.

Dan kemudian beberapa tes yang tampaknya tidak berhubungan crash dengan NullPointerExceptiondan Anda perlu beberapa saat untuk mengetahui bahwa properti gettable sebenarnya tidak ada di sana untuk mendapatkan di tempat pertama.

Meskipun itu masih tidak akan seburuk menemukan masalah dalam produksi.

Mungkin ide yang bagus untuk memastikan semua kelas abstrak Anda memiliki konstruktor. Jika tidak, tes dari pengambil dapat mengingatkan Anda untuk masalah di sana.

Adapun getter dan setter primitif, pertanyaannya mungkin: Apakah saya menguji program saya atau saya menguji JVM atau CLR? Secara umum, JVM tidak perlu diuji.

Alonso del Arte
sumber