Однією з причин є заповітність. Скажіть, у вас цей клас:
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"));
}
}
Для тестування цього потрібні величезні зусилля, або ваш боб намагатиметься підключитися до потокового потоку на кожному тесті. І як тільки у вас з’явиться збій у мережі (або адміністратори в stackoverflow блокують вас через надмірну швидкість доступу), у вас будуть випадкові збої в тестах.
Тож як висновок я б не сказав, що використовувати ApplicationContext
безпосередньо автоматично неправильно, і його слід уникати будь-якою ціною. Однак якщо є кращі варіанти (а є в більшості випадків), тоді використовуйте кращі варіанти.
new MyOtherClass()
об’єкта? Я знаю про @Autowired, але я лише коли-небудь використовував його на полях, і він ламаєтьсяnew MyOtherClass()
..