Saya sedang mengerjakan proyek java. Saya baru dalam pengujian unit. Apa cara terbaik untuk menguji unit metode pribadi di kelas java?
Saya sedang mengerjakan proyek java. Saya baru dalam pengujian unit. Apa cara terbaik untuk menguji unit metode pribadi di kelas java?
Jawaban:
Anda biasanya tidak menguji metode pribadi secara langsung. Karena bersifat pribadi, anggap mereka sebagai detail implementasi. Tidak ada yang akan memanggil salah satu dari mereka dan mengharapkannya bekerja dengan cara tertentu.
Anda sebaiknya menguji antarmuka publik Anda. Jika metode yang memanggil metode pribadi Anda berfungsi seperti yang Anda harapkan, Anda kemudian mengasumsikan dengan ekstensi bahwa metode pribadi Anda berfungsi dengan benar.
Secara umum, saya akan menghindarinya. Jika metode pribadi Anda sangat kompleks sehingga perlu pengujian unit terpisah, itu sering berarti bahwa itu layak kelasnya sendiri. Ini dapat mendorong Anda untuk menulisnya dengan cara yang dapat digunakan kembali. Anda kemudian harus menguji kelas baru dan memanggil antarmuka publik di kelas lama Anda.
Di sisi lain, kadang-kadang memasukkan rincian implementasi ke dalam kelas terpisah mengarah ke kelas dengan antarmuka yang kompleks, banyak data yang lewat antara kelas lama dan baru, atau ke desain yang mungkin terlihat bagus dari sudut pandang OOP, tetapi tidak cocokkan dengan intuisi yang berasal dari domain masalah (mis. memecah model penetapan harga menjadi dua bagian hanya untuk menghindari pengujian metode pribadi yang tidak terlalu intuitif dan dapat menyebabkan masalah di kemudian hari ketika mempertahankan / memperluas kode). Anda tidak ingin memiliki "kelas kembar" yang selalu diubah bersama.
Ketika dihadapkan dengan pilihan antara enkapsulasi dan testabilitas, saya lebih memilih yang kedua. Lebih penting untuk memiliki kode yang benar (yaitu menghasilkan output yang benar) daripada desain OOP bagus yang tidak berfungsi dengan benar, karena tidak diuji secara memadai. Di Java, Anda cukup memberikan metode "default" akses dan meletakkan tes unit dalam paket yang sama. Tes unit hanyalah bagian dari paket yang Anda kembangkan, dan tidak apa-apa untuk memiliki ketergantungan antara tes dan kode yang sedang diuji. Ini berarti bahwa ketika Anda mengubah implementasi, Anda mungkin perlu mengubah tes Anda, tetapi tidak apa-apa - setiap perubahan implementasi memerlukan pengujian ulang kode, dan jika tes perlu dimodifikasi untuk melakukan itu, maka Anda cukup melakukan saya t.
Secara umum, sebuah kelas mungkin menawarkan lebih dari satu antarmuka. Ada antarmuka untuk pengguna, dan antarmuka untuk pengelola. Yang kedua dapat mengekspos lebih banyak untuk memastikan bahwa kode diuji secara memadai. Itu tidak harus menjadi unit test pada metode pribadi - itu bisa, misalnya, logging. Logging juga "merusak enkapsulasi", tetapi kami masih melakukannya, karena sangat berguna.
Pengujian metode pribadi akan tergantung pada kompleksitasnya; beberapa metode privat satu baris tidak akan benar-benar menjamin upaya ekstra pengujian (ini juga dapat dikatakan sebagai metode publik), tetapi beberapa metode pribadi bisa sama rumitnya dengan metode publik, dan sulit untuk diuji melalui antarmuka publik.
Teknik pilihan saya adalah membuat paket metode privat menjadi privat, yang akan memungkinkan akses ke unit test dalam paket yang sama tetapi masih akan dienkapsulasi dari semua kode lainnya. Ini akan memberikan keuntungan dengan menguji logika metode pribadi secara langsung daripada harus bergantung pada uji metode publik untuk mencakup semua bagian (mungkin) logika kompleks.
Jika ini dipasangkan dengan anotasi @VisibleForTesting di pustaka Google Guava, Anda dengan jelas menandai metode pribadi paket ini sebagai terlihat hanya untuk pengujian dan karenanya, tidak boleh dipanggil oleh kelas lain.
Penentang teknik ini berpendapat bahwa ini akan memecah enkapsulasi dan membuka metode pribadi untuk kode dalam paket yang sama. Sementara saya setuju bahwa ini memecah enkapsulasi dan membuka kode privat ke kelas lain, saya berpendapat bahwa pengujian logika kompleks lebih penting daripada enkapsulasi ketat dan tidak menggunakan metode paket privat yang ditandai dengan jelas sebagai terlihat untuk pengujian hanya harus menjadi tanggung jawab pengembang. menggunakan dan mengubah basis kode.
Metode pribadi sebelum pengujian:
private int add(int a, int b){
return a + b;
}
Paket metode pribadi siap untuk pengujian:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
Catatan: Menempatkan tes dalam paket yang sama tidak setara dengan menempatkannya di folder fisik yang sama. Memisahkan kode utama dan kode uji Anda menjadi struktur folder fisik yang terpisah adalah praktik yang baik secara umum, tetapi teknik ini akan berfungsi selama kelas-kelas didefinisikan seperti dalam paket yang sama.
Jika Anda tidak dapat menggunakan API eksternal atau tidak mau, Anda masih dapat menggunakan API JDK standar murni untuk mengakses metode pribadi menggunakan refleksi. Berikut ini sebuah contoh
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Lihat Tutorial Java http://docs.oracle.com/javase/tutorial/reflect/ atau Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary. html untuk informasi lebih lanjut.
Sebagai @ kij dikutip pada jawabannya ada kalanya solusi sederhana menggunakan refleksi benar-benar baik untuk menguji metode pribadi.
Unit test case berarti menguji unit kode. Itu tidak berarti menguji antarmuka karena jika Anda menguji antarmuka, itu tidak berarti Anda menguji unit kode. Itu menjadi semacam pengujian kotak hitam. Selain itu, lebih baik untuk menemukan masalah pada tingkat unit terkecil daripada menentukan masalah pada tingkat antarmuka dan kemudian mencoba men-debug bagian mana yang tidak berfungsi. Oleh karena itu, unit uji kasus harus diuji terlepas dari ruang lingkupnya. Mengikuti adalah cara untuk menguji metode pribadi.
Jika Anda menggunakan java, Anda bisa menggunakan jmockit yang menyediakan Deencapsulation.invoke untuk memanggil metode privat apa pun dari kelas yang sedang diuji. Ini menggunakan refleksi untuk menyebutnya pada akhirnya tetapi memberikan pembungkus yang bagus di sekitarnya. ( https://code.google.com/p/jmockit/ )
Pertama-tama, seperti yang disarankan oleh penulis lain: pikirkan dua kali jika Anda benar-benar perlu menguji metode pribadi. Dan jika demikian, ...
Di .NET Anda dapat mengonversinya menjadi metode "Internal", dan membuat paket " InternalVisible " ke proyek unit test Anda.
Di Jawa Anda dapat menulis tes sendiri di kelas untuk diuji dan metode pengujian Anda harus dapat memanggil metode pribadi juga. Saya tidak benar-benar memiliki pengalaman Java yang besar, jadi itu mungkin bukan praktik terbaik.
Terima kasih.
Saya biasanya membuat metode seperti itu terlindungi. Katakanlah kelas Anda ada di:
src/main/java/you/Class.java
Anda dapat membuat kelas tes sebagai:
src/test/java/you/TestClass.java
Sekarang Anda memiliki akses ke metode yang dilindungi dan dapat unit mengujinya (JUnit atau TestNG tidak terlalu penting), namun Anda menyimpan metode ini dari penelepon yang tidak Anda inginkan.
Perhatikan bahwa ini mengharapkan sumber pohon gaya-maven.
Jika Anda benar-benar perlu menguji metode pribadi, dengan Java yang saya maksud, Anda dapat menggunakan fest assert
dan / atau fest reflect
. Ini menggunakan refleksi.
Impor perpustakaan dengan pakar (versi yang diberikan bukan yang terbaru menurut saya) atau impor langsung di classpath Anda:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
Sebagai contoh, jika Anda memiliki kelas bernama 'MyClass' dengan metode pribadi bernama 'myPrivateMethod' yang menggunakan String sebagai parameter, memperbarui nilainya ke 'this is cool testing!', Anda dapat melakukan tes junit berikut:
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
Perpustakaan ini juga memungkinkan Anda untuk mengganti properti kacang apa pun (tidak peduli mereka bersifat pribadi dan tidak ada setter yang ditulis) oleh tiruan, dan menggunakan ini dengan Mockito atau kerangka tiruan lainnya benar-benar keren. Satu-satunya hal yang harus Anda ketahui saat ini (tidak tahu apakah ini akan lebih baik di versi berikutnya) adalah nama bidang / metode target yang ingin Anda manipulasi, dan tanda tangannya.
Apa yang biasanya saya lakukan di C # adalah membuat metode saya terlindungi dan tidak pribadi. Ini sedikit pengubah akses pribadi, tetapi menyembunyikan metode dari semua kelas yang tidak diwariskan dari kelas yang diuji.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
Setiap kelas yang tidak mewarisi langsung dari classUnderTest tidak tahu bahwa methodToTest bahkan ada. Dalam kode pengujian saya, saya dapat membuat kelas pengujian khusus yang memperluas dan menyediakan akses ke metode ini ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
Kelas ini hanya ada di proyek pengujian saya. Tujuan utamanya adalah untuk menyediakan akses ke metode tunggal ini. Ini memungkinkan saya untuk mengakses tempat-tempat yang tidak dimiliki sebagian besar kelas lain.
protected
adalah bagian dari API publik dan menimbulkan batasan yang sama (tidak pernah dapat diubah, harus didokumentasikan, ...). Di C # Anda bisa menggunakannya internal
bersama InternalsVisibleTo
.
Anda dapat dengan mudah menguji metode pribadi jika Anda menempatkan unit test Anda di kelas dalam pada kelas yang Anda uji. Menggunakan TestNG tes unit Anda harus kelas internal statis publik yang dijelaskan dengan @Test, seperti ini:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
Karena ini adalah kelas dalam, metode pribadi dapat dipanggil.
Tes saya dijalankan dari pakar dan secara otomatis menemukan kasus uji ini. Jika Anda hanya ingin menguji satu kelas yang dapat Anda lakukan
$ mvn test -Dtest=*UserEditorTest
Sumber: http://www.ninthavenue.com.au/how-to-unit-test-private-methods