Ini kodenya:
package com.XXX;
public final class Foo {
private Foo() {
// intentionally empty
}
public static int bar() {
return 1;
}
}
Inilah ujiannya:
package com.XXX;
public FooTest {
@Test
void testValidatesThatBarWorks() {
int result = Foo.bar();
assertEquals(1, result);
}
@Test(expected = java.lang.IllegalAccessException.class)
void testValidatesThatClassFooIsNotInstantiable() {
Class cls = Class.forName("com.XXX.Foo");
cls.newInstance(); // exception here
}
}
Berfungsi dengan baik, kelas telah diuji. Tetapi Cobertura mengatakan bahwa tidak ada cakupan kode dari konstruktor privat kelas. Bagaimana kita bisa menambahkan cakupan tes ke konstruktor pribadi seperti itu?
java
testing
code-coverage
yegor256
sumber
sumber
Jawaban:
Nah, ada cara-cara yang berpotensi Anda gunakan refleksi dll - tetapi apakah itu benar-benar layak? Ini adalah konstruktor yang tidak boleh dipanggil , bukan?
Jika ada anotasi atau sesuatu yang serupa yang dapat Anda tambahkan ke kelas untuk membuat Cobertura mengerti bahwa itu tidak akan dipanggil, lakukan itu: Saya rasa tidak ada gunanya melalui rintangan untuk menambahkan liputan secara artifisial.
EDIT: Jika tidak ada cara untuk melakukannya, jalani saja dengan cakupan yang sedikit berkurang. Ingatlah bahwa pertanggungan dimaksudkan untuk menjadi sesuatu yang berguna bagi Anda - Anda harus bertanggung jawab atas alat tersebut, bukan sebaliknya.
sumber
Saya tidak sepenuhnya setuju dengan Jon Skeet. Saya pikir jika Anda bisa mendapatkan kemenangan mudah untuk memberi Anda liputan dan menghilangkan gangguan dalam laporan liputan Anda, maka Anda harus melakukannya. Beri tahu alat cakupan Anda untuk mengabaikan konstruktor, atau kesampingkan idealisme dan tulis tes berikut dan selesaikan dengannya:
sumber
constructor
? TidakConstructor
boleh diparameterisasi dan bukan tipe mentah?constructor.isAccessible()
selalu mengembalikan nilai salah, bahkan pada konstruktor publik. Seseorang harus menggunakanassertTrue(Modifier.isPrivate(constructor.getModifiers()));
.Meskipun belum tentu untuk cakupan, saya membuat metode ini untuk memverifikasi bahwa kelas utilitas didefinisikan dengan baik dan melakukan sedikit cakupan juga.
Saya telah menempatkan kode lengkap dan contoh di https://github.com/trajano/maven-jee6/tree/master/maven-jee6-test
sumber
Modifier.isPrivate
sepertiisAccessible
yang kembalitrue
untuk konstruktor pribadi dalam beberapa kasus (mengejek gangguan perpustakaan?).Assert.utilityClassWellDefined()
JUnit 4.12+. Sudahkah Anda mempertimbangkan permintaan tarik?setAccessible()
untuk membuat konstruktor dapat diakses menyebabkan masalah untuk alat cakupan kode Sonar (ketika saya melakukan ini kelas menghilang dari laporan cakupan kode Sonar).Saya telah menjadikan private sebagai konstruktor kelas fungsi utilitas statis saya, untuk memenuhi CheckStyle. Tapi seperti poster aslinya, Cobertura mengeluh tentang ujian itu. Awalnya saya mencoba pendekatan ini, tetapi ini tidak mempengaruhi laporan cakupan karena konstruktor tidak pernah benar-benar dijalankan. Jadi sebenarnya semua pengujian ini adalah jika konstruktor tetap bersifat pribadi - dan ini dibuat berlebihan oleh pemeriksaan aksesibilitas di pengujian berikutnya.
Saya mengikuti saran Javid Jamae dan menggunakan refleksi, tetapi menambahkan pernyataan untuk menangkap siapa pun yang mengotak-atik kelas yang sedang diuji (dan menamai tes tersebut untuk menunjukkan Tingkat Kejahatan Tinggi).
Ini sangat berlebihan, tetapi saya harus mengakui bahwa saya menyukai perasaan hangat kabur dari cakupan metode 100%.
sumber
fail(...)
tidak perlu.Dengan Java 8 , Anda dapat menemukan solusi lain.
Saya berasumsi bahwa Anda hanya ingin membuat kelas utilitas dengan beberapa metode statis publik. Jika Anda dapat menggunakan Java 8, Anda dapat menggunakan
interface
sebagai gantinya.Tidak ada konstruktor dan tidak ada keluhan dari Cobertura. Sekarang Anda hanya perlu menguji garis yang benar-benar Anda pedulikan.
sumber
Alasan di balik kode pengujian yang tidak melakukan apa pun adalah untuk mencapai cakupan kode 100% dan untuk memperhatikan ketika cakupan kode turun. Kalau tidak, orang selalu bisa berpikir, hei saya tidak memiliki cakupan kode 100% lagi tetapi MUNGKIN karena konstruktor pribadi saya. Ini membuatnya mudah untuk menemukan metode yang belum diuji tanpa harus memeriksa bahwa itu hanya konstruktor pribadi. Saat basis kode Anda tumbuh, Anda benar-benar akan merasakan perasaan hangat yang menyenangkan melihat 100%, bukan 99%.
IMO lebih baik menggunakan refleksi di sini karena jika tidak, Anda harus mendapatkan alat cakupan kode yang lebih baik yang mengabaikan konstruktor ini atau entah bagaimana memberi tahu alat cakupan kode untuk mengabaikan metode (mungkin Anotasi atau file konfigurasi) karena Anda akan terjebak dengan alat cakupan kode tertentu.
Dalam dunia yang sempurna, semua alat cakupan kode akan mengabaikan konstruktor privat yang termasuk dalam kelas akhir karena konstruktor ada di sana sebagai ukuran "keamanan", tidak ada yang lain :)
Dan kemudian tambahkan kelas ke array saat Anda pergi.Saya akan menggunakan kode ini:
sumber
Versi Cobertura yang lebih baru memiliki dukungan bawaan untuk mengabaikan pengambil / penyetel / konstruktor sepele:
https://github.com/cobertura/cobertura/wiki/Ant-Task-Reference#ignore-trivial
Abaikan Sepele
Abaikan sepele memungkinkan kemampuan untuk mengecualikan konstruktor / metode yang berisi satu baris kode. Beberapa contoh termasuk panggilan ke konstruktor super saja, metode pengambil / penyetel, dll. Untuk menyertakan argumen abaikan sepele, tambahkan berikut ini:
atau dalam build Gradle:
sumber
Jangan. Apa gunanya menguji konstruktor kosong? Karena cobertura 2.0 ada opsi untuk mengabaikan kasus sepele seperti itu (bersama dengan setter / getter), Anda dapat mengaktifkannya di maven dengan menambahkan bagian konfigurasi ke plugin cobertura maven:
Atau Anda dapat menggunakan Cakupan Penjelasan :
@CoverageIgnore
.sumber
Akhirnya, ada solusinya!
sumber
Enum<E>
menjadi benar-benar enum ... Saya percaya itu mengungkapkan niat dengan lebih baik.Saya tidak tahu tentang Cobertura tetapi saya menggunakan Clover dan ini memiliki cara untuk menambahkan pengecualian pencocokan pola. Misalnya, saya memiliki pola yang mengecualikan baris apache-commons-logging sehingga tidak dihitung dalam cakupan.
sumber
Pilihan lainnya adalah membuat penginisialisasi statis yang mirip dengan kode berikut
Dengan cara ini konstruktor pribadi dianggap telah diuji, dan overhead waktu proses pada dasarnya tidak dapat diukur. Saya melakukan ini untuk mendapatkan cakupan 100% menggunakan EclEmma, tetapi kemungkinan itu berhasil untuk setiap alat cakupan. Kelemahan dari solusi ini, tentu saja, adalah Anda menulis kode produksi (penginisialisasi statis) hanya untuk tujuan pengujian.
sumber
ClassUnderTest testClass = Whitebox.invokeConstructor (ClassUnderTest.class);
sumber
Terkadang Cobertura menandai kode yang tidak dimaksudkan untuk dieksekusi sebagai 'tidak tercakup', tidak ada yang salah dengan itu. Mengapa Anda lebih khawatir dengan
99%
cakupan daripada100%
?Secara teknis, Anda masih bisa memanggil konstruktor itu dengan refleksi, tetapi kedengarannya sangat salah bagi saya (dalam kasus ini).
sumber
Jika saya harus menebak maksud pertanyaan Anda, saya akan mengatakan:
Untuk 1, jelas Anda ingin semua inisialisasi dilakukan melalui metode pabrik. Dalam kasus seperti itu, pengujian Anda harus dapat menguji efek samping konstruktor. Ini harus termasuk dalam kategori pengujian metode privat normal. Buat metode menjadi lebih kecil sehingga mereka hanya melakukan sejumlah hal yang menentukan (idealnya, hanya satu hal dan satu hal dengan baik) dan kemudian uji metode yang mengandalkannya.
Misalnya, jika konstruktor [pribadi] saya menyiapkan bidang instance kelas saya
a
ke5
. Lalu saya bisa (atau lebih tepatnya harus) mengujinya:Untuk 2, Anda dapat mengonfigurasi clover untuk mengecualikan konstruktor Util jika Anda memiliki pola penamaan yang ditetapkan untuk kelas Util. Misalnya, dalam proyek saya sendiri, saya menggunakan sesuatu seperti ini (karena kami mengikuti konvensi bahwa nama untuk semua kelas Util harus diakhiri dengan Util):
Saya sengaja meninggalkan
.*
berikut)
karena konstruktor tersebut tidak dimaksudkan untuk memberikan pengecualian (mereka tidak dimaksudkan untuk melakukan apa pun).Tentu saja ada kasus ketiga di mana Anda mungkin ingin memiliki konstruktor kosong untuk kelas non-utilitas. Dalam kasus seperti itu, saya akan merekomendasikan agar Anda meletakkan a
methodContext
dengan tanda tangan konstruktor yang tepat.Jika Anda memiliki banyak kelas luar biasa seperti itu, Anda dapat memilih untuk memodifikasi reg-ex konstruktor privat umum yang saya sarankan dan menghapusnya
Util
. Dalam kasus ini, Anda harus memastikan secara manual bahwa efek samping konstruktor Anda masih diuji dan dicakup oleh metode lain di kelas / proyek Anda.sumber
Test.java adalah file sumber Anda, yang memiliki konstruktor pribadi Anda
sumber
Yang berikut ini bekerja pada saya di kelas yang dibuat dengan penjelasan Lombok @UtilityClass, yang secara otomatis menambahkan konstruktor pribadi.
Meskipun konstruktor.setAccessible (true) seharusnya berfungsi ketika konstruktor privat ditulis secara manual, dengan anotasi Lombok tidak berfungsi, karena ia memaksanya. Constructor.newInstance () sebenarnya menguji bahwa konstruktor dipanggil dan ini melengkapi cakupan costructor itu sendiri. Dengan assertThrows Anda mencegah pengujian gagal dan Anda mengelola pengecualian karena persis seperti kesalahan yang Anda harapkan. Meskipun ini adalah solusi dan saya tidak menghargai konsep "cakupan garis" vs "cakupan fungsionalitas / perilaku", kami dapat menemukan pengertiannya pada tes ini. Bahkan Anda yakin bahwa Kelas Utilitas sebenarnya memiliki Pembuat pribadi yang dengan benar melontarkan pengecualian saat dipanggil juga melalui refleksi. Semoga ini membantu.
sumber
Pilihan yang saya sukai di 2019: Gunakan lombok.
Secara khusus,
@UtilityClass
anotasi . (Sayangnya hanya "eksperimental" pada saat penulisan, tetapi berfungsi dengan baik dan memiliki pandangan positif, sehingga kemungkinan akan segera ditingkatkan ke stabil.)Anotasi ini akan menambahkan konstruktor privat untuk mencegah pembuatan instance dan membuat kelas menjadi final. Saat digabungkan dengan
lombok.addLombokGeneratedAnnotation = true
inlombok.config
, hampir semua framework pengujian akan mengabaikan kode yang dibuat secara otomatis saat menghitung cakupan pengujian, memungkinkan Anda untuk melewati cakupan dari kode yang dibuat secara otomatis tanpa peretasan atau refleksi.sumber
Tidak boleh.
Tampaknya Anda sedang membuat konstruktor pribadi untuk mencegah pembuatan instance kelas yang dimaksudkan untuk hanya berisi metode statis. Daripada mencoba mendapatkan cakupan konstruktor ini (yang mengharuskan kelas dibuat instance-nya), Anda harus membuangnya dan mempercayai developer Anda untuk tidak menambahkan metode instance ke kelas.
sumber