Una de las razones es la comprobabilidad. Digamos que tienes esta clase:
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"));
}
}
¿Cómo puedes probar este frijol? Por ejemplo, así:
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);
}
}
Fácil, verdad?
Si bien aún depende de Spring (debido a las anotaciones), puede eliminar su dependencia de Spring sin cambiar ningún código (solo las definiciones de las anotaciones) y el desarrollador de la prueba no necesita saber nada sobre cómo funciona Spring (tal vez debería de todos modos, pero permite revisar y probar el código por separado de lo que hace spring).
Todavía es posible hacer lo mismo cuando se utiliza el ApplicationContext. Sin embargo, debes burlarte de ApplicationContext
que es una interfaz enorme. Necesita una implementación ficticia o puede usar un marco de imitación como 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);
}
}
Esta es una gran posibilidad, pero creo que la mayoría de la gente estaría de acuerdo en que la primera opción es más elegante y simplifica la prueba.
La única opción que realmente es un problema es esta:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
Probar esto requiere grandes esfuerzos o su bean intentará conectarse a stackoverflow en cada prueba. Y tan pronto como tenga una falla en la red (o los administradores en stackoverflow lo bloqueen debido a una tasa de acceso excesiva), tendrá pruebas de falla al azar.
Como conclusión, no diría que usar el ApplicationContext
directamente es automáticamente incorrecto y debe evitarse a toda costa. Sin embargo, si hay mejores opciones (y las hay en la mayoría de los casos), utilice las mejores opciones.
new MyOtherClass()
objeto? Yo sé de @Autowired, pero yo sólo he utilizado en los campos y se rompe ennew MyOtherClass()
..