เหตุผลหนึ่งคือการตรวจสอบ สมมติว่าคุณมีคลาสนี้:
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"));
}
}
คุณจะทดสอบถั่วนี้ได้อย่างไร เช่นนี้
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);
}
}
ง่ายใช่มั้ย
ในขณะที่คุณยังคงพึ่งพา Spring (เนื่องจากคำอธิบายประกอบ) คุณสามารถลบการพึ่งพา Spring ได้โดยไม่ต้องเปลี่ยนรหัสใด ๆ (เฉพาะคำจำกัดความคำอธิบายประกอบ) และผู้พัฒนาทดสอบไม่จำเป็นต้องรู้อะไรเกี่ยวกับการทำงานของสปริง (บางทีเขาควรจะอยู่ดี แต่ มันช่วยให้สามารถตรวจสอบและทดสอบรหัสแยกต่างหากจากสิ่งที่สปริงทำ)
ยังคงเป็นไปได้ที่จะทำเช่นเดียวกันเมื่อใช้ ApplicationContext อย่างไรก็ตามคุณต้องจำลองApplicationContext
ซึ่งเป็นอินเตอร์เฟสขนาดใหญ่ คุณอาจต้องใช้การจำลองแบบหรือคุณสามารถใช้กรอบการเยาะเย้ยเช่น 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);
}
}
นี่เป็นความเป็นไปได้ค่อนข้างมาก แต่ฉันคิดว่าคนส่วนใหญ่ยอมรับว่าตัวเลือกแรกนั้นดูดีกว่าและทำให้การทดสอบง่ายขึ้น
ตัวเลือกเดียวที่เป็นปัญหาจริงๆคือตัวเลือกนี้:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
การทดสอบนี้ต้องใช้ความพยายามอย่างมากมิเช่นนั้น bean ของคุณกำลังพยายามเชื่อมต่อกับ stackoverflow ในการทดสอบแต่ละครั้ง และทันทีที่คุณมีความล้มเหลวของเครือข่าย (หรือผู้ดูแลระบบที่ stackoverflow บล็อกคุณเนื่องจากอัตราการเข้าถึงที่มากเกินไป) คุณจะได้รับการทดสอบแบบสุ่ม
ดังนั้นโดยสรุปฉันจะไม่พูดว่าการใช้ApplicationContext
โดยตรงนั้นผิดโดยอัตโนมัติและควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมด อย่างไรก็ตามหากมีตัวเลือกที่ดีกว่า (และในกรณีส่วนใหญ่) ให้ใช้ตัวเลือกที่ดีกว่า
new MyOtherClass()
วัตถุหรือไม่? ฉันรู้เกี่ยวกับ @Autowired แต่ฉันเท่านั้นที่เคยใช้มันในทุ่งนาและจะแบ่งบนnew MyOtherClass()
..