Salah satu alasannya adalah testability. Katakanlah Anda memiliki kelas ini:
interface HttpLoader {
String load(String url);
}
interface StringOutput {
void print(String txt);
}
@Component
class MyBean {
@Autowired
MyBean(HttpLoader loader, StringOutput out) {
out.print(loader.load("http://stackoverflow.com"));
}
}
Bagaimana Anda bisa menguji kacang ini? Misal seperti ini:
class MyBeanTest {
public void creatingMyBean_writesStackoverflowPageToOutput() {
// setup
String stackOverflowHtml = "dummy";
StringBuilder result = new StringBuilder();
// execution
new MyBean(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get, result::append);
// evaluation
assertEquals(result.toString(), stackOverflowHtml);
}
}
Mudah kan?
Meskipun Anda masih bergantung pada Pegas (karena anotasi), Anda dapat menghapus ketergantungan Anda pada pegas tanpa mengubah kode apa pun (hanya definisi anotasi) dan pengembang pengujian tidak perlu tahu apa pun tentang cara kerja pegas (mungkin ia harus melakukannya, tetapi memungkinkan untuk meninjau dan menguji kode secara terpisah dari apa yang dilakukan pegas).
Masih dimungkinkan untuk melakukan hal yang sama saat menggunakan ApplicationContext. Namun, Anda perlu mengejek ApplicationContext
yang merupakan antarmuka besar. Anda membutuhkan implementasi dummy atau Anda dapat menggunakan kerangka kerja mengejek seperti Mockito:
@Component
class MyBean {
@Autowired
MyBean(ApplicationContext context) {
HttpLoader loader = context.getBean(HttpLoader.class);
StringOutput out = context.getBean(StringOutput.class);
out.print(loader.load("http://stackoverflow.com"));
}
}
class MyBeanTest {
public void creatingMyBean_writesStackoverflowPageToOutput() {
// setup
String stackOverflowHtml = "dummy";
StringBuilder result = new StringBuilder();
ApplicationContext context = Mockito.mock(ApplicationContext.class);
Mockito.when(context.getBean(HttpLoader.class))
.thenReturn(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get);
Mockito.when(context.getBean(StringOutput.class)).thenReturn(result::append);
// execution
new MyBean(context);
// evaluation
assertEquals(result.toString(), stackOverflowHtml);
}
}
Ini kemungkinan yang cukup, tetapi saya pikir kebanyakan orang akan setuju bahwa opsi pertama lebih elegan dan membuat tes lebih sederhana.
Satu-satunya opsi yang benar-benar masalah adalah yang ini:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
Menguji ini membutuhkan upaya besar atau kacang Anda akan mencoba untuk terhubung ke stackoverflow pada setiap pengujian. Dan segera setelah Anda mengalami kegagalan jaringan (atau admin di stackoverflow memblokir Anda karena tingkat akses yang berlebihan), Anda akan mengalami tes yang gagal secara acak.
Jadi sebagai kesimpulan saya tidak akan mengatakan bahwa menggunakan ApplicationContext
secara langsung itu salah dan harus dihindari di semua biaya. Namun jika ada opsi yang lebih baik (dan ada dalam banyak kasus), maka gunakan opsi yang lebih baik.
new MyOtherClass()
objek? Aku tahu tentang @ Autowired, tapi aku hanya pernah digunakan pada bidang dan istirahat padanew MyOtherClass()
..